diff --git a/.gitignore b/.gitignore index eb61c88c8a..57aaf99cc6 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ libtool *.tmp src/dsql/parse.cpp .vscode/.browse.VC.db +extern/decNumber/libdecFloat.a diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a65a677ae..def6b77e5e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,103 @@ # v4.0 Alpha 1 (unreleased) +## New features + +* [CORE-5568](http://tracker.firebirdsql.org/browse/CORE-5568): SQL SECURITY feature + Reference(s): [/doc/sql.extentions/README.sql_security](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.sql_security.txt) + Contributor(s): Roman Simakov + +* [CORE-5525](http://tracker.firebirdsql.org/browse/CORE-5525): Create high-precision floating point datatype named DECFLOAT + Reference(s): [/doc/sql.extentions/README.data_types](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.data_types) + Contributor(s): Alex Peshkoff + +* [CORE-5488](http://tracker.firebirdsql.org/browse/CORE-5488): Timeouts for running SQL statements and idle connections + Reference(s): [/doc/README.session_idle_timeouts](https://github.com/FirebirdSQL/firebird/raw/master/doc/README.session_idle_timeouts), [/doc/README.statement_timeouts](https://github.com/FirebirdSQL/firebird/raw/master/doc/README.statement_timeouts) + Contributor(s): Vlad Khorsun + +* [CORE-5463](http://tracker.firebirdsql.org/browse/CORE-5463): Support DEFAULT context value in INSERT, UPDATE, MERGE and UPDATE OR INSERT statements + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-5449](http://tracker.firebirdsql.org/browse/CORE-5449): Support GENERATED ALWAYS identity columns and OVERRIDE clause + Reference(s): [doc/sql.extensions/README.identity_columns](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.identity_columns.txt) + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-5346](http://tracker.firebirdsql.org/browse/CORE-5346): Named windows + Reference(s): [doc/sql.extensions/README.window_functions](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.window_functions.md) + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-5343](http://tracker.firebirdsql.org/browse/CORE-5343): Allow particular DBA privileges to be transferred to regular users + Reference(s): [doc/sql.extensions/README.builtin_functions](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.builtin_functions.txt), [doc/sql.extensions/README.ddl](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.ddl.txt) + Contributor(s): Alex Peshkoff + +* [CORE-3647](http://tracker.firebirdsql.org/browse/CORE-3647): Frames for window functions + Reference(s): [doc/sql.extensions/README.window_functions](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.window_functions.md) + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-2762](http://tracker.firebirdsql.org/browse/CORE-2762): New built-in function to check whether some role is implicitly active + Reference(s): [doc/sql.extensions/README.cumulative_roles](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.cumulative_roles.txt) + Contributor(s): Roman Simakov + +* [CORE-1815](http://tracker.firebirdsql.org/browse/CORE-1815): Ability to grant role to another role + Reference(s): [doc/sql.extensions/README.cumulative_roles](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.cumulative_roles.txt) + Contributor(s): Roman Simakov + +* [CORE-1688](http://tracker.firebirdsql.org/browse/CORE-1688): More ANSI SQL:2003 window functions (PERCENK_RANK, CUME_DIST, NTILE) + Reference(s): [doc/sql.extensions/README.window_functions](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.window_functions.md) + Contributor(s): Hajime Nakagami, Adriano dos Santos Fernandes + +* [CORE-751](http://tracker.firebirdsql.org/browse/CORE-751): Implicitly active roles (and their permissions summarized) + Contributor(s): Roman Simakov + +## Improvements + +* [CORE-5431](http://tracker.firebirdsql.org/browse/CORE-5431): Support for DROP IDENTITY clause + Reference(s): [doc/sql.extensions/README.identity_columns](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.identity_columns.txt) + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-5430](http://tracker.firebirdsql.org/browse/CORE-5430): Support for INCREMENT option in identity columns + Reference(s): [doc/sql.extensions/README.identity_columns](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.identity_columns.txt) + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-5119](http://tracker.firebirdsql.org/browse/CORE-5119): Support autocommit mode in SET TRANSACTION statement + Contributor(s): Dmitry Yemanov + +* [CORE-5064](http://tracker.firebirdsql.org/browse/CORE-5064): Add datatypes (VAR)BINARY(n) and BINARY VARYING(n) as aliases for (VAR)CHAR(n) CHARACTER SET OCTETS + Contributor(s): Dimitry Sibiryakov + +* [CORE-2557](http://tracker.firebirdsql.org/browse/CORE-2557): Grants on MON$ tables + Contributor(s): Alex Peshkoff + +* [CORE-2216](http://tracker.firebirdsql.org/browse/CORE-2216): NBackup as online dump + Contributor(s): Roman Simakov, Vlad Khorsun + +* [CORE-2192](http://tracker.firebirdsql.org/browse/CORE-2192): Extend maximum database page size to 32KB + Contributor(s): Dmitry Yemanov + +* [CORE-2040](http://tracker.firebirdsql.org/browse/CORE-2040): Allow exception name and possibly exception text to be determined within a "WHEN ANY" error handling block + Reference(s): [doc/sql.extensions/README.context_variables](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.context_variables) + Contributor(s): Dmitry Yemanov + +* [CORE-1132](http://tracker.firebirdsql.org/browse/CORE-1132): Exception context in PSQL exception handlers + Reference(s): [doc/sql.extensions/README.context_variables](https://github.com/FirebirdSQL/firebird/raw/master/doc/sql.extensions/README.context_variables) + Contributor(s): Dmitry Yemanov + +* [CORE-749](http://tracker.firebirdsql.org/browse/CORE-749): Increase maximum length of object names to 63 characters + Contributor(s): Adriano dos Santos Fernandes + ## Bugfixes +* [CORE-5545](http://tracker.firebirdsql.org/browse/CORE-5545): Wrong syntax with CREATE TRIGGER ... ON used with POSITION + Contributor(s): Adriano dos Santos Fernandes + +* [CORE-5454](http://tracker.firebirdsql.org/browse/CORE-5454): INSERT into updatable view without explicit field list failed + Contributor(s): Adriano dos Santos Fernandes + * [CORE-5408](http://tracker.firebirdsql.org/browse/CORE-5408): Result of boolean expression can not be concatenated with string literal Contributor(s): Adriano dos Santos Fernandes +* [CORE-5404](http://tracker.firebirdsql.org/browse/CORE-5404): Inconsistent column/line references when PSQL definitions return errors + Contributor(s): Adriano dos Santos Fernandes + * [CORE-5237](http://tracker.firebirdsql.org/browse/CORE-5237): Invalid handling of dot (.) and asterisk (*) in config file name and path for include clause Contributor(s): Dimitry Sibiryakov @@ -26,58 +119,3 @@ * [CORE-4424](http://tracker.firebirdsql.org/browse/CORE-4424): Rollback to wrong savepoint if several exception handlers on the same level are executed Contributor(s): Dimitry Sibiryakov -## Improvements - -* [CORE-5431](http://tracker.firebirdsql.org/browse/CORE-5431): Support for DROP IDENTITY clause - Contributor(s): Adriano dos Santos Fernandes - -* [CORE-5430](http://tracker.firebirdsql.org/browse/CORE-5430): Support for INCREMENT option in identity columns - Contributor(s): Adriano dos Santos Fernandes - -* [CORE-5119](http://tracker.firebirdsql.org/browse/CORE-5119): Support autocommit mode in SET TRANSACTION statement - Contributor(s): Dmitry Yemanov - -* [CORE-2557](http://tracker.firebirdsql.org/browse/CORE-2557): Grants on MON$ tables - Contributor(s): Alex Peshkoff - -* [CORE-2216](http://tracker.firebirdsql.org/browse/CORE-2216): NBackup as online dump - Contributor(s): Roman Simakov, Vlad Khorsun - -* [CORE-2192](http://tracker.firebirdsql.org/browse/CORE-2192): Extend maximum database page size to 32KB - Contributor(s): Dmitry Yemanov - -* [CORE-2040](http://tracker.firebirdsql.org/browse/CORE-2040): Allow exception name and possibly exception text to be determined within a "WHEN ANY" error handling block - Contributor(s): Dmitry Yemanov - -* [CORE-1132](http://tracker.firebirdsql.org/browse/CORE-1132): Exception context in PSQL exception handlers - Contributor(s): Dmitry Yemanov - -* [CORE-749](http://tracker.firebirdsql.org/browse/CORE-749): Increase maximum length of object names to 63 characters - Contributor(s): Adriano dos Santos Fernandes - -## New features - -* [CORE-5346](http://tracker.firebirdsql.org/browse/CORE-5346): Named windows - Contributor(s): Adriano dos Santos Fernandes - -* [CORE-5343](http://tracker.firebirdsql.org/browse/CORE-5343): Allow particular DBA privileges to be transferred to regular users - Contributor(s): Alex Peshkoff - -* [CORE-3647](http://tracker.firebirdsql.org/browse/CORE-3647): Frames for window functions - Contributor(s): Adriano dos Santos Fernandes - -* [CORE-2990](http://tracker.firebirdsql.org/browse/CORE-2990): Physical (page-level) standby replication / page shipping - Contributor(s): Roman Simakov, Vlad Khorsun - -* [CORE-2762](http://tracker.firebirdsql.org/browse/CORE-2762): New built-in function to check whether some role is implicitly active - Contributor(s): Roman Simakov - -* [CORE-1815](http://tracker.firebirdsql.org/browse/CORE-1815): Ability to grant role to another role - Contributor(s): Roman Simakov - -* [CORE-1688](http://tracker.firebirdsql.org/browse/CORE-1688): More ANSI SQL:2003 window functions (PERCENK_RANK, CUME_DIST, NTILE) - Contributor(s): Hajime Nakagami, Adriano dos Santos Fernandes - -* [CORE-751](http://tracker.firebirdsql.org/browse/CORE-751): Implicitly active roles (and their permissions summarized) - Contributor(s): Roman Simakov - diff --git a/builds/install/arch-specific/linux/makeInstallImage.sh.in b/builds/install/arch-specific/linux/makeInstallImage.sh.in index 70b3f7af5d..0cf0fbe504 100644 --- a/builds/install/arch-specific/linux/makeInstallImage.sh.in +++ b/builds/install/arch-specific/linux/makeInstallImage.sh.in @@ -206,7 +206,7 @@ copyFiles() { cp $BuiltFBDir/examples/empbuild/*.fdb ${TargetDir}@FB_SAMPLEDBDIR@ ReadOnlyTree ${TargetDir}@FB_SAMPLEDIR@ - chmod 0444 ${TargetDir}@FB_SAMPLEDBDIR@/*.fdb + chmod 0664 ${TargetDir}@FB_SAMPLEDBDIR@/*.fdb fi #QLI help diff --git a/builds/make.new/config/install-sh b/builds/make.new/config/install-sh index 377bb8687f..0b0fdcbba6 100755 --- a/builds/make.new/config/install-sh +++ b/builds/make.new/config/install-sh @@ -1,7 +1,7 @@ #!/bin/sh # install - install a program, script, or datafile -scriptversion=2011-11-20.07; # UTC +scriptversion=2013-12-25.23; # UTC # This originates from X11R5 (mit/util/scripts/install.sh), which was # later released in X11R6 (xc/config/util/install.sh) with the @@ -41,19 +41,15 @@ scriptversion=2011-11-20.07; # UTC # This script is compatible with the BSD install script, but was written # from scratch. +tab=' ' nl=' ' -IFS=" "" $nl" +IFS=" $tab$nl" -# set DOITPROG to echo to test this script +# Set DOITPROG to "echo" to test this script. -# Don't use :- since 4.3BSD and earlier shells don't like it. doit=${DOITPROG-} -if test -z "$doit"; then - doit_exec=exec -else - doit_exec=$doit -fi +doit_exec=${doit:-exec} # Put in absolute file names if you don't have them in your path; # or use environment vars. @@ -68,17 +64,6 @@ mvprog=${MVPROG-mv} rmprog=${RMPROG-rm} stripprog=${STRIPPROG-strip} -posix_glob='?' -initialize_posix_glob=' - test "$posix_glob" != "?" || { - if (set -f) 2>/dev/null; then - posix_glob= - else - posix_glob=: - fi - } -' - posix_mkdir= # Desired mode of installed file. @@ -97,7 +82,7 @@ dir_arg= dst_arg= copy_on_change=false -no_target_directory= +is_target_a_directory=possibly usage="\ Usage: $0 [OPTION]... [-T] SRCFILE DSTFILE @@ -137,46 +122,57 @@ while test $# -ne 0; do -d) dir_arg=true;; -g) chgrpcmd="$chgrpprog $2" - shift;; + shift;; --help) echo "$usage"; exit $?;; -m) mode=$2 - case $mode in - *' '* | *' '* | *' -'* | *'*'* | *'?'* | *'['*) - echo "$0: invalid mode: $mode" >&2 - exit 1;; - esac - shift;; + case $mode in + *' '* | *"$tab"* | *"$nl"* | *'*'* | *'?'* | *'['*) + echo "$0: invalid mode: $mode" >&2 + exit 1;; + esac + shift;; -o) chowncmd="$chownprog $2" - shift;; + shift;; -s) stripcmd=$stripprog;; - -t) dst_arg=$2 - # Protect names problematic for 'test' and other utilities. - case $dst_arg in - -* | [=\(\)!]) dst_arg=./$dst_arg;; - esac - shift;; + -t) + is_target_a_directory=always + dst_arg=$2 + # Protect names problematic for 'test' and other utilities. + case $dst_arg in + -* | [=\(\)!]) dst_arg=./$dst_arg;; + esac + shift;; - -T) no_target_directory=true;; + -T) is_target_a_directory=never;; --version) echo "$0 $scriptversion"; exit $?;; - --) shift - break;; + --) shift + break;; - -*) echo "$0: invalid option: $1" >&2 - exit 1;; + -*) echo "$0: invalid option: $1" >&2 + exit 1;; *) break;; esac shift done +# We allow the use of options -d and -T together, by making -d +# take the precedence; this is for compatibility with GNU install. + +if test -n "$dir_arg"; then + if test -n "$dst_arg"; then + echo "$0: target directory not allowed when installing a directory." >&2 + exit 1 + fi +fi + if test $# -ne 0 && test -z "$dir_arg$dst_arg"; then # When -d is used, all remaining arguments are directories to create. # When -t is used, the destination is already specified. @@ -207,6 +203,15 @@ if test $# -eq 0; then exit 0 fi +if test -z "$dir_arg"; then + if test $# -gt 1 || test "$is_target_a_directory" = always; then + if test ! -d "$dst_arg"; then + echo "$0: $dst_arg: Is not a directory." >&2 + exit 1 + fi + fi +fi + if test -z "$dir_arg"; then do_exit='(exit $ret); exit $ret' trap "ret=129; $do_exit" 1 @@ -223,16 +228,16 @@ if test -z "$dir_arg"; then *[0-7]) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw='% 200' + u_plus_rw='% 200' fi cp_umask=`expr '(' 777 - $mode % 1000 ')' $u_plus_rw`;; *) if test -z "$stripcmd"; then - u_plus_rw= + u_plus_rw= else - u_plus_rw=,u+rw + u_plus_rw=,u+rw fi cp_umask=$mode$u_plus_rw;; esac @@ -269,41 +274,15 @@ do # If destination is a directory, append the input filename; won't work # if double slashes aren't ignored. if test -d "$dst"; then - if test -n "$no_target_directory"; then - echo "$0: $dst_arg: Is a directory" >&2 - exit 1 + if test "$is_target_a_directory" = never; then + echo "$0: $dst_arg: Is a directory" >&2 + exit 1 fi dstdir=$dst dst=$dstdir/`basename "$src"` dstdir_status=0 else - # Prefer dirname, but fall back on a substitute if dirname fails. - dstdir=` - (dirname "$dst") 2>/dev/null || - expr X"$dst" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ - X"$dst" : 'X\(//\)[^/]' \| \ - X"$dst" : 'X\(//\)$' \| \ - X"$dst" : 'X\(/\)' \| . 2>/dev/null || - echo X"$dst" | - sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ - s//\1/ - q - } - /^X\(\/\/\)[^/].*/{ - s//\1/ - q - } - /^X\(\/\/\)$/{ - s//\1/ - q - } - /^X\(\/\).*/{ - s//\1/ - q - } - s/.*/./; q' - ` - + dstdir=`dirname "$dst"` test -d "$dstdir" dstdir_status=$? fi @@ -314,74 +293,74 @@ do if test $dstdir_status != 0; then case $posix_mkdir in '') - # Create intermediate dirs using mode 755 as modified by the umask. - # This is like FreeBSD 'install' as of 1997-10-28. - umask=`umask` - case $stripcmd.$umask in - # Optimize common cases. - *[2367][2367]) mkdir_umask=$umask;; - .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; + # Create intermediate dirs using mode 755 as modified by the umask. + # This is like FreeBSD 'install' as of 1997-10-28. + umask=`umask` + case $stripcmd.$umask in + # Optimize common cases. + *[2367][2367]) mkdir_umask=$umask;; + .*0[02][02] | .[02][02] | .[02]) mkdir_umask=22;; - *[0-7]) - mkdir_umask=`expr $umask + 22 \ - - $umask % 100 % 40 + $umask % 20 \ - - $umask % 10 % 4 + $umask % 2 - `;; - *) mkdir_umask=$umask,go-w;; - esac + *[0-7]) + mkdir_umask=`expr $umask + 22 \ + - $umask % 100 % 40 + $umask % 20 \ + - $umask % 10 % 4 + $umask % 2 + `;; + *) mkdir_umask=$umask,go-w;; + esac - # With -d, create the new directory with the user-specified mode. - # Otherwise, rely on $mkdir_umask. - if test -n "$dir_arg"; then - mkdir_mode=-m$mode - else - mkdir_mode= - fi + # With -d, create the new directory with the user-specified mode. + # Otherwise, rely on $mkdir_umask. + if test -n "$dir_arg"; then + mkdir_mode=-m$mode + else + mkdir_mode= + fi - posix_mkdir=false - case $umask in - *[123567][0-7][0-7]) - # POSIX mkdir -p sets u+wx bits regardless of umask, which - # is incompatible with FreeBSD 'install' when (umask & 300) != 0. - ;; - *) - tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ - trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 + posix_mkdir=false + case $umask in + *[123567][0-7][0-7]) + # POSIX mkdir -p sets u+wx bits regardless of umask, which + # is incompatible with FreeBSD 'install' when (umask & 300) != 0. + ;; + *) + tmpdir=${TMPDIR-/tmp}/ins$RANDOM-$$ + trap 'ret=$?; rmdir "$tmpdir/d" "$tmpdir" 2>/dev/null; exit $ret' 0 - if (umask $mkdir_umask && - exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 - then - if test -z "$dir_arg" || { - # Check for POSIX incompatibilities with -m. - # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or - # other-writable bit of parent directory when it shouldn't. - # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. - ls_ld_tmpdir=`ls -ld "$tmpdir"` - case $ls_ld_tmpdir in - d????-?r-*) different_mode=700;; - d????-?--*) different_mode=755;; - *) false;; - esac && - $mkdirprog -m$different_mode -p -- "$tmpdir" && { - ls_ld_tmpdir_1=`ls -ld "$tmpdir"` - test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" - } - } - then posix_mkdir=: - fi - rmdir "$tmpdir/d" "$tmpdir" - else - # Remove any dirs left behind by ancient mkdir implementations. - rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null - fi - trap '' 0;; - esac;; + if (umask $mkdir_umask && + exec $mkdirprog $mkdir_mode -p -- "$tmpdir/d") >/dev/null 2>&1 + then + if test -z "$dir_arg" || { + # Check for POSIX incompatibilities with -m. + # HP-UX 11.23 and IRIX 6.5 mkdir -m -p sets group- or + # other-writable bit of parent directory when it shouldn't. + # FreeBSD 6.1 mkdir -m -p sets mode of existing directory. + ls_ld_tmpdir=`ls -ld "$tmpdir"` + case $ls_ld_tmpdir in + d????-?r-*) different_mode=700;; + d????-?--*) different_mode=755;; + *) false;; + esac && + $mkdirprog -m$different_mode -p -- "$tmpdir" && { + ls_ld_tmpdir_1=`ls -ld "$tmpdir"` + test "$ls_ld_tmpdir" = "$ls_ld_tmpdir_1" + } + } + then posix_mkdir=: + fi + rmdir "$tmpdir/d" "$tmpdir" + else + # Remove any dirs left behind by ancient mkdir implementations. + rmdir ./$mkdir_mode ./-p ./-- 2>/dev/null + fi + trap '' 0;; + esac;; esac if $posix_mkdir && ( - umask $mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" + umask $mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir" ) then : else @@ -391,53 +370,51 @@ do # directory the slow way, step by step, checking for races as we go. case $dstdir in - /*) prefix='/';; - [-=\(\)!]*) prefix='./';; - *) prefix='';; + /*) prefix='/';; + [-=\(\)!]*) prefix='./';; + *) prefix='';; esac - eval "$initialize_posix_glob" - oIFS=$IFS IFS=/ - $posix_glob set -f + set -f set fnord $dstdir shift - $posix_glob set +f + set +f IFS=$oIFS prefixes= for d do - test X"$d" = X && continue + test X"$d" = X && continue - prefix=$prefix$d - if test -d "$prefix"; then - prefixes= - else - if $posix_mkdir; then - (umask=$mkdir_umask && - $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break - # Don't fail if two instances are running concurrently. - test -d "$prefix" || exit 1 - else - case $prefix in - *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; - *) qprefix=$prefix;; - esac - prefixes="$prefixes '$qprefix'" - fi - fi - prefix=$prefix/ + prefix=$prefix$d + if test -d "$prefix"; then + prefixes= + else + if $posix_mkdir; then + (umask=$mkdir_umask && + $doit_exec $mkdirprog $mkdir_mode -p -- "$dstdir") && break + # Don't fail if two instances are running concurrently. + test -d "$prefix" || exit 1 + else + case $prefix in + *\'*) qprefix=`echo "$prefix" | sed "s/'/'\\\\\\\\''/g"`;; + *) qprefix=$prefix;; + esac + prefixes="$prefixes '$qprefix'" + fi + fi + prefix=$prefix/ done if test -n "$prefixes"; then - # Don't fail if two instances are running concurrently. - (umask $mkdir_umask && - eval "\$doit_exec \$mkdirprog $prefixes") || - test -d "$dstdir" || exit 1 - obsolete_mkdir_used=true + # Don't fail if two instances are running concurrently. + (umask $mkdir_umask && + eval "\$doit_exec \$mkdirprog $prefixes") || + test -d "$dstdir" || exit 1 + obsolete_mkdir_used=true fi fi fi @@ -472,15 +449,12 @@ do # If -C, don't bother to copy if it wouldn't change the file. if $copy_on_change && - old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && - new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && - - eval "$initialize_posix_glob" && - $posix_glob set -f && + old=`LC_ALL=C ls -dlL "$dst" 2>/dev/null` && + new=`LC_ALL=C ls -dlL "$dsttmp" 2>/dev/null` && + set -f && set X $old && old=:$2:$4:$5:$6 && set X $new && new=:$2:$4:$5:$6 && - $posix_glob set +f && - + set +f && test "$old" = "$new" && $cmpprog "$dst" "$dsttmp" >/dev/null 2>&1 then @@ -493,24 +467,24 @@ do # to itself, or perhaps because mv is so ancient that it does not # support -f. { - # Now remove or move aside any old file at destination location. - # We try this two ways since rm can't unlink itself on some - # systems and the destination file might be busy for other - # reasons. In this case, the final cleanup might fail but the new - # file should still install successfully. - { - test ! -f "$dst" || - $doit $rmcmd -f "$dst" 2>/dev/null || - { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && - { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } - } || - { echo "$0: cannot unlink or rename $dst" >&2 - (exit 1); exit 1 - } - } && + # Now remove or move aside any old file at destination location. + # We try this two ways since rm can't unlink itself on some + # systems and the destination file might be busy for other + # reasons. In this case, the final cleanup might fail but the new + # file should still install successfully. + { + test ! -f "$dst" || + $doit $rmcmd -f "$dst" 2>/dev/null || + { $doit $mvcmd -f "$dst" "$rmtmp" 2>/dev/null && + { $doit $rmcmd -f "$rmtmp" 2>/dev/null; :; } + } || + { echo "$0: cannot unlink or rename $dst" >&2 + (exit 1); exit 1 + } + } && - # Now rename the file to the real destination. - $doit $mvcmd "$dsttmp" "$dst" + # Now rename the file to the real destination. + $doit $mvcmd "$dsttmp" "$dst" } fi || exit 1 diff --git a/builds/posix/Makefile.in b/builds/posix/Makefile.in index a8e17a130e..6014a3748b 100644 --- a/builds/posix/Makefile.in +++ b/builds/posix/Makefile.in @@ -66,7 +66,7 @@ else GCC=gcc endif -.PHONY: master_process cross_process firebird Debug Release extern +.PHONY: master_process cross_process firebird Debug Release external all: firebird @@ -130,10 +130,13 @@ $(GEN_ROOT)/Make.Version : $(SRC_ROOT)/misc/writeBuildNum.sh # build external software # -extern: +external: $(MAKE) -C $(ROOT)/extern/btyacc $(MAKE) -C $(ROOT)/extern/cloop TARGET=release WITH_FPC=0 BUILD_DIR=$(TMP_ROOT)/cloop OUT_DIR=$(GEN_ROOT)/$(TARGET)/cloop + $(MAKE) -C $(ROOT)/extern/decNumber + ln -sf $(ROOT)/extern/decNumber/libdecFloat.a $(LIB) + ifeq ($(TOMBUILD_FLG),Y) CFLAGS="$(CFLAGS)" $(MAKE) -C $(ROOT)/extern/libtommath -f makefile.shared GCC=$(GCC) ln -sf $(TOMMATH_SO).$(TOMMATH_VER) $(LIB) @@ -180,7 +183,7 @@ master_process: ln -sf $(SRC_ROOT)/include/gen/autoconfig.auto $(SRC_ROOT)/include/gen/autoconfig.h $(MAKE) updateBuildNum $(MAKE) export_lists - $(MAKE) extern + $(MAKE) external $(MAKE) updateCloopInterfaces $(MAKE) boot $(MAKE) yvalve @@ -215,7 +218,7 @@ cross1: ln -sf $(SRC_ROOT)/include/gen/autoconfig.auto $(SRC_ROOT)/include/gen/autoconfig.h $(MAKE) updateBuildNum $(MAKE) export_lists - $(MAKE) extern + $(MAKE) external $(MAKE) updateCloopInterfaces $(MAKE) boot $(MAKE) yvalve @@ -657,11 +660,12 @@ install install-embedded silent_install package packages dist: # .PHONY: clean clean_objects clean_dependancies clean_extern_objects clean_build \ clean_gpre_gen clean_icu clean_dbs clean_examples clean_makefiles \ - clean_editline clean_all + clean_editline clean_all clean_decfloat clean: clean_objects clean_dependancies clean_extern_objects clean_build \ - clean_yacc_gen clean_gpre_gen clean_dbs clean_examples clean_tommath + clean_yacc_gen clean_gpre_gen clean_dbs clean_examples clean_tommath \ + clean_decfloat ifeq ($(EDITLINE_FLG),Y) ifeq ($(STD_EDITLINE),false) @@ -701,6 +705,9 @@ clean_editline: clean_tommath: -$(MAKE) -C $(ROOT)/extern/libtommath clean +clean_decfloat: + -$(MAKE) -C $(ROOT)/extern/decNumber clean + clean_objects: $(RM) `find $(TMP_ROOT)/ -type f -name '*.o' -print` $(RM) `find $(TMP_ROOT)/ -type f -name '*.a' -print` diff --git a/builds/posix/make.android.arme b/builds/posix/make.android.arme index c968944253..4547bb7590 100644 --- a/builds/posix/make.android.arme +++ b/builds/posix/make.android.arme @@ -56,7 +56,6 @@ LDFLAGS += --sysroot=$(CROSS_PLATFORM) -L$(NDK)/sources/cxx-stl/gnu-libstdc++/$( DroidLibs := -lm -ldl -lsupc++ LINK_LIBS = $(DroidLibs) -STATICLINK_LIBS = $(DroidLibs) SO_LINK_LIBS = $(DroidLibs) EXE_LINK_OPTIONS= $(LDFLAGS) $(UNDEF_FLAGS) $(LIB_PATH_OPTS) $(LINK_EMPTY_SYMBOLS) diff --git a/builds/posix/make.defaults b/builds/posix/make.defaults index 681717f346..145792cf9b 100755 --- a/builds/posix/make.defaults +++ b/builds/posix/make.defaults @@ -132,6 +132,7 @@ CAS_OPTIONS=@CAS_OPTIONS@ # multiple-precision integer library MATHLIB=@MATHLIB@ +DECLIB=-ldecFloat LSB_FLG=@LSB_FLG@ ifeq ($(LSB_FLG), Y) @@ -177,9 +178,8 @@ STATICLIB_LINK = $(AR) crus EXE_LINK = $(CXX) $(GLOB_OPTIONS) $(CXXFLAGS) STATICEXE_LINK = $(CXX) $(GLOB_OPTIONS) $(CXXFLAGS) -LINK_LIBS = @LIBS@ -STATICLINK_LIBS = @LIBS@ -SO_LINK_LIBS = @LIBS@ +LINK_LIBS = @LIBS@ $(DECLIB) +SO_LINK_LIBS = @LIBS@ $(DECLIB) # Default extensions diff --git a/builds/posix/make.platform.solaris.examples b/builds/posix/make.platform.solaris.examples index 3c9321aa2a..b0a20a54db 100644 --- a/builds/posix/make.platform.solaris.examples +++ b/builds/posix/make.platform.solaris.examples @@ -30,7 +30,6 @@ DEV_FLAGS=$(COMM_SOLX_FLAGS) -ggdb -g3 -p $(WARNINGS) ifdef SFIO LINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio -STATICLINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio SO_LINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio endif diff --git a/builds/posix/make.shared.variables b/builds/posix/make.shared.variables index 2d3143fc49..54888bf9fe 100644 --- a/builds/posix/make.shared.variables +++ b/builds/posix/make.shared.variables @@ -1,8 +1,8 @@ # Helper functions doObjects= $(patsubst %.y,%.o,$(patsubst %.epp,%.o,$(patsubst %.c,%.o,$(1:.cpp=.o)))) makeObjects= $(addprefix $(OBJ)/$(1)/,$(call doObjects,$2)) -dirFiles= $(notdir $(wildcard ../src/$(1)/*.cpp)) $(notdir $(wildcard ../src/$(1)/*.c)) \ - $(notdir $(wildcard ../src/$(1)/*.epp)) $(notdir $(wildcard ../src/$(1)/*.y)) +dirFiles= $(sort $(notdir $(wildcard ../src/$(1)/*.cpp)) $(notdir $(wildcard ../src/$(1)/*.c)) \ + $(notdir $(wildcard ../src/$(1)/*.epp)) $(notdir $(wildcard ../src/$(1)/*.y))) dirInPath= $(call makeObjects,$(1),$(call dirFiles,$(1))) dirMaster= $(call dirInPath,$(1)) diff --git a/builds/posix/prefix.solx86gcc b/builds/posix/prefix.solx86gcc index 570fe400dc..f6464b4dba 100644 --- a/builds/posix/prefix.solx86gcc +++ b/builds/posix/prefix.solx86gcc @@ -36,7 +36,6 @@ PROD_FLAGS=-w $(COMM_SOLX_FLAGS) -O2 -march=pentium DEV_FLAGS=$(COMM_SOLX_FLAGS) -ggdb -g3 -p $(WARNINGS) ifdef SFIO LINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio -STATICLINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio SO_LINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio endif diff --git a/builds/posix/prefix.solx86gcc_64 b/builds/posix/prefix.solx86gcc_64 index 30a396c76c..1585b964dd 100644 --- a/builds/posix/prefix.solx86gcc_64 +++ b/builds/posix/prefix.solx86gcc_64 @@ -27,7 +27,7 @@ # ./configure --build=x86_64-pc-solaris2.10 # copy libncurses.a from the ncurses build to gen/firebird/lib # make sure that make.defaults uses -lncurses instead of -lcurses for -# LINK_LIBS and STATICLINK_LIBS e.g. -lncurses -leditline +# LINK_LIBS and SO_LINK_LIBS e.g. -lncurses -leditline WARNINGS=-Wall -Wno-switch -Wno-parentheses -Wno-unused-variable -Wno-non-virtual-dtor COMM_SOLX_FLAGS:=-DSOLARIS -DBSD_COMP -DAMD64 -pipe -fmessage-length=0 -MMD -fPIC @@ -46,7 +46,6 @@ PROD_FLAGS=-w $(COMM_SOLX_FLAGS) -O2 DEV_FLAGS=$(COMM_SOLX_FLAGS) -ggdb $(WARNINGS) ifdef SFIO LINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio -STATICLINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio SO_LINK_LIBS+=$(SFIO_LDFLAGS) -lstdio -lsfio endif diff --git a/builds/win32/make_boot.bat b/builds/win32/make_boot.bat index ea8ce10ef0..6c62dcb18e 100644 --- a/builds/win32/make_boot.bat +++ b/builds/win32/make_boot.bat @@ -38,6 +38,9 @@ if "%ERRLEV%"=="1" goto :END call :LibTomMath if "%ERRLEV%"=="1" goto :END +call :decNumber +if "%ERRLEV%"=="1" goto :END + call :zlib if "%ERRLEV%"=="1" goto :END @@ -133,6 +136,21 @@ if errorlevel 1 call :boot2 libtommath_%FB_OBJ_DIR% @call set_build_target.bat %* goto :EOF +::=================== +:: BUILD decNumber +:decNumber +@echo. +@call set_build_target.bat %* RELEASE +@echo Building decNumber (%FB_OBJ_DIR%)... +@call compile.bat %FB_ROOT_PATH%\extern\decNumber\msvc\decNumber_MSVC%MSVC_VERSION% decNumber_%FB_OBJ_DIR%_%FB_TARGET_PLATFORM%.log decNumber +if errorlevel 1 call :boot2 decNumber_%FB_OBJ_DIR% +@call set_build_target.bat %* DEBUG +@echo Building decNumber (%FB_OBJ_DIR%)... +@call compile.bat %FB_ROOT_PATH%\extern\decNumber\msvc\decNumber_MSVC%MSVC_VERSION% decNumber_%FB_OBJ_DIR%_%FB_TARGET_PLATFORM%.log decNumber +if errorlevel 1 call :boot2 decNumber_%FB_OBJ_DIR% +@call set_build_target.bat %* +goto :EOF + ::=================== :: Extract zlib :zlib diff --git a/builds/win32/msvc12/common.vcxproj b/builds/win32/msvc12/common.vcxproj index 89d9da898e..7fe6f7c086 100644 --- a/builds/win32/msvc12/common.vcxproj +++ b/builds/win32/msvc12/common.vcxproj @@ -58,6 +58,7 @@ + @@ -154,6 +155,7 @@ + @@ -283,7 +285,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\Release\tommath.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) @@ -299,7 +301,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\Debug\tommath.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) @@ -318,7 +320,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\Release\tommath.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) @@ -336,7 +338,7 @@ 0x041d - ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\Debug\tommath.lib;%(AdditionalDependencies) + ws2_32.lib;../../../extern/libtommath/lib/$(PlatformName)\$(Configuration)\tommath.lib;../../../extern/decNumber/lib/$(PlatformName)\$(Configuration)\decnumber.lib;%(AdditionalDependencies) diff --git a/builds/win32/msvc12/common.vcxproj.filters b/builds/win32/msvc12/common.vcxproj.filters index fb87986102..8fec78de3a 100644 --- a/builds/win32/msvc12/common.vcxproj.filters +++ b/builds/win32/msvc12/common.vcxproj.filters @@ -216,6 +216,9 @@ common + + classes + @@ -530,5 +533,8 @@ headers + + headers + \ No newline at end of file diff --git a/configure.ac b/configure.ac index d8bb4a30cb..b89067f521 100644 --- a/configure.ac +++ b/configure.ac @@ -870,6 +870,16 @@ dnl if 64-bit mode, then archive tool, ar, needs -X64 option [AC_LANG_PROGRAM(,[[int lp64 = __64BIT__; ]])], [AR_OPTIONS=-X64],) fi + +XE_SAVE_ENV() +CXXFLAGS="$CXXFLAGS -std=c++11" +AC_CACHE_CHECK([whether the C++ compiler understands noexcept], [ac_cv_cxx_noexcept], [ + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[int f(int x) noexcept { return x + 1; }]], [[]])], + [ac_cv_cxx_noexcept=yes], [ac_cv_cxx_noexcept=no])]) +XE_RESTORE_ENV() +if test "$ac_cv_cxx_noexcept" = yes; then + AC_DEFINE([HAS_NOEXCEPT], [1], [Define if the C++ compiler understands noexcept.]) +fi AC_LANG_POP(C++) dnl Check for functions diff --git a/doc/sql.extensions/README.data_types b/doc/sql.extensions/README.data_types index 050e702696..553e24d533 100644 --- a/doc/sql.extensions/README.data_types +++ b/doc/sql.extensions/README.data_types @@ -101,3 +101,87 @@ BINARY, VARBINARY, BINARY VARYING (FB 4.0) 2. Can be distinguished from text types by value 1 in RDB$FIELD_SUB_TYPE. 3. Character set is set to OCTETS for backward compatibility. 4. In API are similar to corresponding text types, getSubType() returns 0. + + +DECFLOAT (FB 4.0) +-------------- + + Function: + DB2-compliant numeric type. DECFLOAT precisely (unlike FLOAT or DOUBLE PRECISION that provide + binary approximation) stores decimal values being therefore ideal choice for business appli- + cations. Firebird according to IEEE standard has both 16- and 34-digit decimal float encodings. + All intermediate calculations are performed with 34-digit values. + + Author: + Alex Peshkoff + + Syntax rules: + DECFLOAT(16) + DECFLOAT(34) + + Storage: + 64-bit / 128-bit, format according to IEEE 754. + + Example(s): + 1. DECLARE VARIABLE VAR1 DECFLOAT(34); + 2. CREATE TABLE TABLE1 (FIELD1 DECFLOAT(16)); + + Note(s): + 1. A number of standard functions can be used with DECFLOAT datatype. It is: + ABS, CEILING, EXP, FLOOR, LN, LOG, LOG10, POWER, SIGN, SQRT. + Agregate functions SUM, AVG, MAX and MIN also work with DECFLOAT data. + All statistics aggregates (like but not limited to STDDEV or CORR) work with DECFLOAT data. + + 2. Firebird supports four functions, specially designed to support DECFLOAT data: + - COMPARE_DECFLOAT - compares two DECFLOAT values to be equal, different or unordered. + Returns SMALLINT value which can be as follows: + 0 - values are equal + 1 - first value is less than second + 2 - first value is greater than second + 3 - values unordered (i.e. one or both is NAN / SNAN) + Unlike comparison operators ('<', '=', '>', etc.) comparison is exact - i.e. + COMPARE_DECFLOAT(2.17, 2.170) returns 2, not 0. + - NORMALIZE_DECFLOAT - has single DECFLOAT argument returned in it's simplest form. That + means that for any nonzero value trailing zero are removed with appropriate correction + of an exponent. For example NORMALIZE_DECFLOAT(12.00) returns 12 and + NORMALIZE_DECFLOAT(120) returns 1.2E+2. + - QUANTIZE - has two DECFLOAT arguments. The returned value is first argument scaled using + second value as a pattern. For example QUANTIZE(1234, 9.999) returns 1234.000. + - TOTALORDER - compares two DECFLOAT values including any special value. The comparison is + exact. Returns SMALLINT value which can be as follows: + -1 - first value is less than second + 0 - values are equal + 1 - first value is greater than second + DECFLOAT values are ordered as follows: + -nan < -snan < -inf < -0.1 < -0.10 < -0 < 0 < 0.10 < 0.1 < inf < snan < nan + + 3. Firebird supports new session control operator SET DECFLOAT. It has following forms: + SET DECFLOAT ROUND - controls rounding mode used in operations with DECFLOAT + values. Valid modes are: CEILING (towards +infinity), UP (away from 0), HALF_UP + (to nearest, if equidistant - up), HALF_EVEN (to nearest, if equidistant - ensure + last digit in the result to be even), HALF_DOWN (to nearest, if equidistant - down), + DOWN (towards 0), FLOOR (towards -infinity), REROUND (up if digit to be rounded is + 0 or 5, down in other cases). + + SET DECFLOAT TRAPS TO - controls which + exceptional conditions cause a trap. Valid traps are: Division_by_zero, Inexact, + Invalid_operation, Overflow and Underflow. By default traps are set to: + Division_by_zero, Invalid_operation, Overflow, Underflow. + + SET DECFLOAT BIND - controls how are DECFLOAT values represented in outer + world (i.e. in messages or in XSQLDA). Valid binding types are: NATIVE (use IEEE754 + binary representation), CHAR/CHARACTER (use ASCII string), DOUBLE PRECISION (use + 8-byte FP representation - same as used for DOUBLE PRECISION fields) or BIGINT + with possible comma-separated SCALE clause (i.e. 'BIGINT, 3'). Various bindings + are useful if one plans to use DECFLOAT values with some old client not supporting + native format. One can choose between strings (ideal precision, but poor support + for further processing), floating point values (ideal support for further processing + but poor precision) or scaled integers (good support for further processing and + required precision but range of values is very limited). When using is a tool like + generic purporse GUI client choice of CHAR binding is OK in most cases. + + 4. The length of DECFLOAT literals are limited to 1024 characters. For longer values, you will + need to use the scientific notation. For example, the 0.0<1020 zeroes>11 cannot be used + as a literal, instead you can use the equivalent in scientific notation: 1.1E-1022. + Similarly 10<1022 zeroes>0 can be presented as 1.0E1024. + diff --git a/examples/interfaces/06.fb_message.cpp b/examples/interfaces/06.fb_message.cpp index 239d794322..9d76658a30 100644 --- a/examples/interfaces/06.fb_message.cpp +++ b/examples/interfaces/06.fb_message.cpp @@ -4,6 +4,9 @@ * DESCRIPTION: A sample of using static messages. * Prints user-defined tables with comments. * + * Example for the following interface: + * IDecFloat16 - support for IEEE-754 64-bit decimal float numbers + * * Example for the following macro: * * FB_MESSAGE - defines static messages @@ -32,6 +35,7 @@ #include static IMaster* master = fb_get_master_interface(); +static IDecFloat16* idf = NULL; int main() { @@ -51,6 +55,8 @@ int main() try { + idf = master->getUtilInterface()->getDecFloat16(&status); + att = prov->attachDatabase(&status, dbName, 0, NULL); tra = att->startTransaction(&status, 0, NULL); @@ -72,13 +78,15 @@ int main() (FB_SMALLINT, relationId) (FB_VARCHAR(31), relationName) (FB_VARCHAR(100), description) + (FB_DECFLOAT16, df16) ) output(&status, master); input.clear(); input->systemFlag = 0; rs = att->openCursor(&status, tra, 0, - "select rdb$relation_id, rdb$relation_name, rdb$description" + "select rdb$relation_id, rdb$relation_name, rdb$description," + " cast (rdb$relation_id as decfloat(16)) * 0.05 as df16" " from rdb$relations" " where rdb$system_flag = ?" " order by rdb$relation_id", @@ -89,10 +97,13 @@ int main() { unsigned lRelName = output->relationNameNull ? 0 : output->relationName.length; unsigned lDesc = output->descriptionNull ? 0 : output->description.length; - printf("%4d %*.*s%c%*.*s\n", output->relationId, + char t[IDecFloat16::STRING_SIZE]; + idf->toStr(&output->df16, t); + + printf("%4d %*.*s%c%*.*s (%s)\n", output->relationId, lRelName, lRelName, output->relationName.str, lDesc ? '/' : ' ', - lDesc, lDesc, output->description.str); + lDesc, lDesc, output->description.str, t); } rs->close(&status); diff --git a/extern/cloop/src/tests/test1/CTest.c b/extern/cloop/src/tests/test1/CTest.c index a3739ce411..b2a5b00b55 100644 --- a/extern/cloop/src/tests/test1/CTest.c +++ b/extern/cloop/src/tests/test1/CTest.c @@ -20,7 +20,7 @@ */ #include "CalcCApi.h" -#include +#include #include #ifdef WIN32 diff --git a/extern/decNumber/.gitignore b/extern/decNumber/.gitignore new file mode 100644 index 0000000000..ac8f109172 --- /dev/null +++ b/extern/decNumber/.gitignore @@ -0,0 +1,2 @@ +temp/ +lib/ diff --git a/extern/decNumber/ICU-license.html b/extern/decNumber/ICU-license.html new file mode 100644 index 0000000000..00d356d452 --- /dev/null +++ b/extern/decNumber/ICU-license.html @@ -0,0 +1,45 @@ + + + + +ICU License - ICU 1.8.1 and later + + + +

ICU License - ICU 1.8.1 and later

+
+COPYRIGHT AND PERMISSION NOTICE
+
+Copyright (c) 1995-2005 International Business Machines Corporation and others
+All rights reserved.
+
+Permission is hereby granted, free of charge, to any person obtaining a
+copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, and/or sell copies of the Software, and to permit persons
+to whom the Software is furnished to do so, provided that the above
+copyright notice(s) and this permission notice appear in all copies of
+the Software and that both the above copyright notice(s) and this
+permission notice appear in supporting documentation.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
+HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL
+INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING
+FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder
+shall not be used in advertising or otherwise to promote the sale, use
+or other dealings in this Software without prior written authorization
+of the copyright holder.
+
+--------------------------------------------------------------------------------
+All trademarks and registered trademarks mentioned herein are the property of their respective owners.
+
+ + diff --git a/extern/decNumber/Makefile b/extern/decNumber/Makefile new file mode 100644 index 0000000000..7aa18ad805 --- /dev/null +++ b/extern/decNumber/Makefile @@ -0,0 +1,11 @@ +LIBRARY=libdecFloat.a + +$(LIBRARY): $(wildcard *.c) $(wildcard *.h) Makefile + $(RM) -f *.o + $(CC) -c -O3 -fPIC $(subst decCommon.c,,$(subst decBasic.c,,$(wildcard *.c))) + $(AR) crs $(LIBRARY) *.o + $(RM) -f *.o + +.PHONY: clean +clean: + $(RM) -f *.o $(LIBRARY) diff --git a/extern/decNumber/decBasic.c b/extern/decNumber/decBasic.c new file mode 100644 index 0000000000..56396f8da7 --- /dev/null +++ b/extern/decNumber/decBasic.c @@ -0,0 +1,3908 @@ +/* ------------------------------------------------------------------ */ +/* decBasic.c -- common base code for Basic decimal types */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises code that is shared between decDouble and */ +/* decQuad (but not decSingle). The main arithmetic operations are */ +/* here (Add, Subtract, Multiply, FMA, and Division operators). */ +/* */ +/* Unlike decNumber, parameterization takes place at compile time */ +/* rather than at runtime. The parameters are set in the decDouble.c */ +/* (etc.) files, which then include this one to produce the compiled */ +/* code. The functions here, therefore, are code shared between */ +/* multiple formats. */ +/* */ +/* This must be included after decCommon.c. */ +/* ------------------------------------------------------------------ */ +// Names here refer to decFloat rather than to decDouble, etc., and +// the functions are in strict alphabetical order. + +// The compile-time flags SINGLE, DOUBLE, and QUAD are set up in +// decCommon.c +#if !defined(QUAD) + #error decBasic.c must be included after decCommon.c +#endif +#if SINGLE + #error Routines in decBasic.c are for decDouble and decQuad only +#endif + +/* Private constants */ +#define DIVIDE 0x80000000 // Divide operations [as flags] +#define REMAINDER 0x40000000 // .. +#define DIVIDEINT 0x20000000 // .. +#define REMNEAR 0x10000000 // .. + +/* Private functions (local, used only by routines in this module) */ +static decFloat *decDivide(decFloat *, const decFloat *, + const decFloat *, decContext *, uInt); +static decFloat *decCanonical(decFloat *, const decFloat *); +static void decFiniteMultiply(bcdnum *, uByte *, const decFloat *, + const decFloat *); +static decFloat *decInfinity(decFloat *, const decFloat *); +static decFloat *decInvalid(decFloat *, decContext *); +static decFloat *decNaNs(decFloat *, const decFloat *, const decFloat *, + decContext *); +static Int decNumCompare(const decFloat *, const decFloat *, Flag); +static decFloat *decToIntegral(decFloat *, const decFloat *, decContext *, + enum rounding, Flag); +static uInt decToInt32(const decFloat *, decContext *, enum rounding, + Flag, Flag); + +/* ------------------------------------------------------------------ */ +/* decCanonical -- copy a decFloat, making canonical */ +/* */ +/* result gets the canonicalized df */ +/* df is the decFloat to copy and make canonical */ +/* returns result */ +/* */ +/* This is exposed via decFloatCanonical for Double and Quad only. */ +/* This works on specials, too; no error or exception is possible. */ +/* ------------------------------------------------------------------ */ +static decFloat * decCanonical(decFloat *result, const decFloat *df) { + uInt encode, precode, dpd; // work + uInt inword, uoff, canon; // .. + Int n; // counter (down) + if (df!=result) *result=*df; // effect copy if needed + if (DFISSPECIAL(result)) { + if (DFISINF(result)) return decInfinity(result, df); // clean Infinity + // is a NaN + DFWORD(result, 0)&=~ECONNANMASK; // clear ECON except selector + if (DFISCCZERO(df)) return result; // coefficient continuation is 0 + // drop through to check payload + } + // return quickly if the coefficient continuation is canonical + { // declare block + #if DOUBLE + uInt sourhi=DFWORD(df, 0); + uInt sourlo=DFWORD(df, 1); + if (CANONDPDOFF(sourhi, 8) + && CANONDPDTWO(sourhi, sourlo, 30) + && CANONDPDOFF(sourlo, 20) + && CANONDPDOFF(sourlo, 10) + && CANONDPDOFF(sourlo, 0)) return result; + #elif QUAD + uInt sourhi=DFWORD(df, 0); + uInt sourmh=DFWORD(df, 1); + uInt sourml=DFWORD(df, 2); + uInt sourlo=DFWORD(df, 3); + if (CANONDPDOFF(sourhi, 4) + && CANONDPDTWO(sourhi, sourmh, 26) + && CANONDPDOFF(sourmh, 16) + && CANONDPDOFF(sourmh, 6) + && CANONDPDTWO(sourmh, sourml, 28) + && CANONDPDOFF(sourml, 18) + && CANONDPDOFF(sourml, 8) + && CANONDPDTWO(sourml, sourlo, 30) + && CANONDPDOFF(sourlo, 20) + && CANONDPDOFF(sourlo, 10) + && CANONDPDOFF(sourlo, 0)) return result; + #endif + } // block + + // Loop to repair a non-canonical coefficent, as needed + inword=DECWORDS-1; // current input word + uoff=0; // bit offset of declet + encode=DFWORD(result, inword); + for (n=DECLETS-1; n>=0; n--) { // count down declets of 10 bits + dpd=encode>>uoff; + uoff+=10; + if (uoff>32) { // crossed uInt boundary + inword--; + encode=DFWORD(result, inword); + uoff-=32; + dpd|=encode<<(10-uoff); // get pending bits + } + dpd&=0x3ff; // clear uninteresting bits + if (dpd<0x16e) continue; // must be canonical + canon=BIN2DPD[DPD2BIN[dpd]]; // determine canonical declet + if (canon==dpd) continue; // have canonical declet + // need to replace declet + if (uoff>=10) { // all within current word + encode&=~(0x3ff<<(uoff-10)); // clear the 10 bits ready for replace + encode|=canon<<(uoff-10); // insert the canonical form + DFWORD(result, inword)=encode; // .. and save + continue; + } + // straddled words + precode=DFWORD(result, inword+1); // get previous + precode&=0xffffffff>>(10-uoff); // clear top bits + DFWORD(result, inword+1)=precode|(canon<<(32-(10-uoff))); + encode&=0xffffffff<>(10-uoff); // insert canonical + DFWORD(result, inword)=encode; // .. and save + } // n + return result; + } // decCanonical + +/* ------------------------------------------------------------------ */ +/* decDivide -- divide operations */ +/* */ +/* result gets the result of dividing dfl by dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* op is the operation selector */ +/* returns result */ +/* */ +/* op is one of DIVIDE, REMAINDER, DIVIDEINT, or REMNEAR. */ +/* ------------------------------------------------------------------ */ +#define DIVCOUNT 0 // 1 to instrument subtractions counter +#define DIVBASE ((uInt)BILLION) // the base used for divide +#define DIVOPLEN DECPMAX9 // operand length ('digits' base 10**9) +#define DIVACCLEN (DIVOPLEN*3) // accumulator length (ditto) +static decFloat * decDivide(decFloat *result, const decFloat *dfl, + const decFloat *dfr, decContext *set, uInt op) { + decFloat quotient; // for remainders + bcdnum num; // for final conversion + uInt acc[DIVACCLEN]; // coefficent in base-billion .. + uInt div[DIVOPLEN]; // divisor in base-billion .. + uInt quo[DIVOPLEN+1]; // quotient in base-billion .. + uByte bcdacc[(DIVOPLEN+1)*9+2]; // for quotient in BCD, +1, +1 + uInt *msua, *msud, *msuq; // -> msu of acc, div, and quo + Int divunits, accunits; // lengths + Int quodigits; // digits in quotient + uInt *lsua, *lsuq; // -> current acc and quo lsus + Int length, multiplier; // work + uInt carry, sign; // .. + uInt *ua, *ud, *uq; // .. + uByte *ub; // .. + uInt uiwork; // for macros + uInt divtop; // top unit of div adjusted for estimating + #if DIVCOUNT + static uInt maxcount=0; // worst-seen subtractions count + uInt divcount=0; // subtractions count [this divide] + #endif + + // calculate sign + num.sign=(DFWORD(dfl, 0)^DFWORD(dfr, 0)) & DECFLOAT_Sign; + + if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr)) { // either is special? + // NaNs are handled as usual + if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + // one or two infinities + if (DFISINF(dfl)) { + if (DFISINF(dfr)) return decInvalid(result, set); // Two infinities bad + if (op&(REMAINDER|REMNEAR)) return decInvalid(result, set); // as is rem + // Infinity/x is infinite and quiet, even if x=0 + DFWORD(result, 0)=num.sign; + return decInfinity(result, result); + } + // must be x/Infinity -- remainders are lhs + if (op&(REMAINDER|REMNEAR)) return decCanonical(result, dfl); + // divides: return zero with correct sign and exponent depending + // on op (Etiny for divide, 0 for divideInt) + decFloatZero(result); + if (op==DIVIDEINT) DFWORD(result, 0)|=num.sign; // add sign + else DFWORD(result, 0)=num.sign; // zeros the exponent, too + return result; + } + // next, handle zero operands (x/0 and 0/x) + if (DFISZERO(dfr)) { // x/0 + if (DFISZERO(dfl)) { // 0/0 is undefined + decFloatZero(result); + DFWORD(result, 0)=DECFLOAT_qNaN; + set->status|=DEC_Division_undefined; + return result; + } + if (op&(REMAINDER|REMNEAR)) return decInvalid(result, set); // bad rem + set->status|=DEC_Division_by_zero; + DFWORD(result, 0)=num.sign; + return decInfinity(result, result); // x/0 -> signed Infinity + } + num.exponent=GETEXPUN(dfl)-GETEXPUN(dfr); // ideal exponent + if (DFISZERO(dfl)) { // 0/x (x!=0) + // if divide, result is 0 with ideal exponent; divideInt has + // exponent=0, remainders give zero with lower exponent + if (op&DIVIDEINT) { + decFloatZero(result); + DFWORD(result, 0)|=num.sign; // add sign + return result; + } + if (!(op&DIVIDE)) { // a remainder + // exponent is the minimum of the operands + num.exponent=MINI(GETEXPUN(dfl), GETEXPUN(dfr)); + // if the result is zero the sign shall be sign of dfl + num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign; + } + bcdacc[0]=0; + num.msd=bcdacc; // -> 0 + num.lsd=bcdacc; // .. + return decFinalize(result, &num, set); // [divide may clamp exponent] + } // 0/x + // [here, both operands are known to be finite and non-zero] + + // extract the operand coefficents into 'units' which are + // base-billion; the lhs is high-aligned in acc and the msu of both + // acc and div is at the right-hand end of array (offset length-1); + // the quotient can need one more unit than the operands as digits + // in it are not necessarily aligned neatly; further, the quotient + // may not start accumulating until after the end of the initial + // operand in acc if that is small (e.g., 1) so the accumulator + // must have at least that number of units extra (at the ls end) + GETCOEFFBILL(dfl, acc+DIVACCLEN-DIVOPLEN); + GETCOEFFBILL(dfr, div); + // zero the low uInts of acc + acc[0]=0; + acc[1]=0; + acc[2]=0; + acc[3]=0; + #if DOUBLE + #if DIVOPLEN!=2 + #error Unexpected Double DIVOPLEN + #endif + #elif QUAD + acc[4]=0; + acc[5]=0; + acc[6]=0; + acc[7]=0; + #if DIVOPLEN!=4 + #error Unexpected Quad DIVOPLEN + #endif + #endif + + // set msu and lsu pointers + msua=acc+DIVACCLEN-1; // [leading zeros removed below] + msuq=quo+DIVOPLEN; + //[loop for div will terminate because operands are non-zero] + for (msud=div+DIVOPLEN-1; *msud==0;) msud--; + // the initial least-significant unit of acc is set so acc appears + // to have the same length as div. + // This moves one position towards the least possible for each + // iteration + divunits=(Int)(msud-div+1); // precalculate + lsua=msua-divunits+1; // initial working lsu of acc + lsuq=msuq; // and of quo + + // set up the estimator for the multiplier; this is the msu of div, + // plus two bits from the unit below (if any) rounded up by one if + // there are any non-zero bits or units below that [the extra two + // bits makes for a much better estimate when the top unit is small] + divtop=*msud<<2; + if (divunits>1) { + uInt *um=msud-1; + uInt d=*um; + if (d>=750000000) {divtop+=3; d-=750000000;} + else if (d>=500000000) {divtop+=2; d-=500000000;} + else if (d>=250000000) {divtop++; d-=250000000;} + if (d) divtop++; + else for (um--; um>=div; um--) if (*um) { + divtop++; + break; + } + } // >1 unit + + #if DECTRACE + {Int i; + printf("----- div="); + for (i=divunits-1; i>=0; i--) printf("%09ld ", (LI)div[i]); + printf("\n");} + #endif + + // now collect up to DECPMAX+1 digits in the quotient (this may + // need OPLEN+1 uInts if unaligned) + quodigits=0; // no digits yet + for (;; lsua--) { // outer loop -- each input position + #if DECCHECK + if (lsua=lsua;) msua--; + accunits=(Int)(msua-lsua+1); // [maybe 0] + // subtraction is only necessary and possible if there are as + // least as many units remaining in acc for this iteration as + // there are in div + if (accunitsdiv: subtraction necessary at this position + for (ud=msud, ua=msua; ud>div; ud--, ua--) if (*ud!=*ua) break; + // [now at first mismatch or lsu] + if (*ud>*ua) break; // next time... + if (*ud==*ua) { // all compared equal + *lsuq+=1; // increment result + msua=lsua; // collapse acc units + *msua=0; // .. to a zero + break; + } + + // subtraction necessary; estimate multiplier [see above] + // if both *msud and *msua are small it is cost-effective to + // bring in part of the following units (if any) to get a + // better estimate (assume some other non-zero in div) + #define DIVLO 1000000U + #define DIVHI (DIVBASE/DIVLO) + #if DECUSE64 + if (divunits>1) { + // there cannot be a *(msud-2) for DECDOUBLE so next is + // an exact calculation unless DECQUAD (which needs to + // assume bits out there if divunits>2) + uLong mul=(uLong)*msua * DIVBASE + *(msua-1); + uLong div=(uLong)*msud * DIVBASE + *(msud-1); + #if QUAD + if (divunits>2) div++; + #endif + mul/=div; + multiplier=(Int)mul; + } + else multiplier=*msua/(*msud); + #else + if (divunits>1 && *msuadivunits + // msud is one unit 'lower' than msua, so estimate differently + #if DECUSE64 + uLong mul; + // as before, bring in extra digits if possible + if (divunits>1 && *msua>DIVSHIFTA); + carry=(uInt)(((uLong)hop*DIVMAGIC)>>DIVSHIFTB); + // the estimate is now in hi; now calculate sub-hi*10**9 + // to get the remainder (which will be =DIVBASE) { + lo-=DIVBASE; // correct by +1 + carry++; + } + } + #else // 32-bit + uInt hi; + // calculate multiplier*(*ud) into hi and lo + LONGMUL32HI(hi, *ud, multiplier); // get the high word + lo=multiplier*(*ud); // .. and the low + lo+=carry; // add the old hi + carry=hi+(lo=DIVBASE) { // split is needed + hop=(carry<<3)+(lo>>DIVSHIFTA); // hi:lo/2**29 + LONGMUL32HI(carry, hop, DIVMAGIC); // only need the high word + // [DIVSHIFTB is 32, so carry can be used directly] + // the estimate is now in carry; now calculate hi:lo-est*10**9; + // happily the top word of the result is irrelevant because it + // will always be zero so this needs only one multiplication + lo-=(carry*DIVBASE); + // the correction here will be at most +1; do it + if (lo>=DIVBASE) { + lo-=DIVBASE; + carry++; + } + } + #endif + if (lo>*ua) { // borrow needed + *ua+=DIVBASE; + carry++; + } + *ua-=lo; + } // ud loop + if (carry) *ua-=carry; // accdigits>divdigits [cannot borrow] + } // inner loop + + // the outer loop terminates when there is either an exact result + // or enough digits; first update the quotient digit count and + // pointer (if any significant digits) + #if DECTRACE + if (*lsuq || quodigits) printf("*lsuq=%09ld\n", (LI)*lsuq); + #endif + if (quodigits) { + quodigits+=9; // had leading unit earlier + lsuq--; + if (quodigits>DECPMAX+1) break; // have enough + } + else if (*lsuq) { // first quotient digits + const uInt *pow; + for (pow=DECPOWERS; *lsuq>=*pow; pow++) quodigits++; + lsuq--; + // [cannot have >DECPMAX+1 on first unit] + } + + if (*msua!=0) continue; // not an exact result + // acc is zero iff used all of original units and zero down to lsua + // (must also continue to original lsu for correct quotient length) + if (lsua>acc+DIVACCLEN-DIVOPLEN) continue; + for (; msua>lsua && *msua==0;) msua--; + if (*msua==0 && msua==lsua) break; + } // outer loop + + // all of the original operand in acc has been covered at this point + // quotient now has at least DECPMAX+2 digits + // *msua is now non-0 if inexact and sticky bits + // lsuq is one below the last uint of the quotient + lsuq++; // set -> true lsu of quo + if (*msua) *lsuq|=1; // apply sticky bit + + // quo now holds the (unrounded) quotient in base-billion; one + // base-billion 'digit' per uInt. + #if DECTRACE + printf("DivQuo:"); + for (uq=msuq; uq>=lsuq; uq--) printf(" %09ld", (LI)*uq); + printf("\n"); + #endif + + // Now convert to BCD for rounding and cleanup, starting from the + // most significant end [offset by one into bcdacc to leave room + // for a possible carry digit if rounding for REMNEAR is needed] + for (uq=msuq, ub=bcdacc+1; uq>=lsuq; uq--, ub+=9) { + uInt top, mid, rem; // work + if (*uq==0) { // no split needed + UBFROMUI(ub, 0); // clear 9 BCD8s + UBFROMUI(ub+4, 0); // .. + *(ub+8)=0; // .. + continue; + } + // *uq is non-zero -- split the base-billion digit into + // hi, mid, and low three-digits + #define divsplit9 1000000 // divisor + #define divsplit6 1000 // divisor + // The splitting is done by simple divides and remainders, + // assuming the compiler will optimize these [GCC does] + top=*uq/divsplit9; + rem=*uq%divsplit9; + mid=rem/divsplit6; + rem=rem%divsplit6; + // lay out the nine BCD digits (plus one unwanted byte) + UBFROMUI(ub, UBTOUI(&BIN2BCD8[top*4])); + UBFROMUI(ub+3, UBTOUI(&BIN2BCD8[mid*4])); + UBFROMUI(ub+6, UBTOUI(&BIN2BCD8[rem*4])); + } // BCD conversion loop + ub--; // -> lsu + + // complete the bcdnum; quodigits is correct, so the position of + // the first non-zero is known + num.msd=bcdacc+1+(msuq-lsuq+1)*9-quodigits; + num.lsd=ub; + + // make exponent adjustments, etc + if (lsuamaxcount) { // new high-water nark + maxcount=divcount; + printf("DivNewMaxCount: %ld\n", (LI)maxcount); + } + #endif + + if (op&DIVIDE) return decFinalize(result, &num, set); // all done + + // Is DIVIDEINT or a remainder; there is more to do -- first form + // the integer (this is done 'after the fact', unlike as in + // decNumber, so as not to tax DIVIDE) + + // The first non-zero digit will be in the first 9 digits, known + // from quodigits and num.msd, so there is always space for DECPMAX + // digits + + length=(Int)(num.lsd-num.msd+1); + //printf("Length exp: %ld %ld\n", (LI)length, (LI)num.exponent); + + if (length+num.exponent>DECPMAX) { // cannot fit + decFloatZero(result); + DFWORD(result, 0)=DECFLOAT_qNaN; + set->status|=DEC_Division_impossible; + return result; + } + + if (num.exponent>=0) { // already an int, or need pad zeros + for (ub=num.lsd+1; ub<=num.lsd+num.exponent; ub++) *ub=0; + num.lsd+=num.exponent; + } + else { // too long: round or truncate needed + Int drop=-num.exponent; + if (!(op&REMNEAR)) { // simple truncate + num.lsd-=drop; + if (num.lsd re-round digit + uByte reround; // reround value + *(num.msd-1)=0; // in case of left carry, or make 0 + if (drop 0] + reround=*roundat; + for (ub=roundat+1; ub<=num.lsd; ub++) { + if (*ub!=0) { + reround=DECSTICKYTAB[reround]; + break; + } + } // check stickies + if (roundat>num.msd) num.lsd=roundat-1; + else { + num.msd--; // use the 0 .. + num.lsd=num.msd; // .. at the new MSD place + } + if (reround!=0) { // discarding non-zero + uInt bump=0; + // rounding is DEC_ROUND_HALF_EVEN always + if (reround>5) bump=1; // >0.5 goes up + else if (reround==5) // exactly 0.5000 .. + bump=*(num.lsd) & 0x01; // .. up iff [new] lsd is odd + if (bump!=0) { // need increment + // increment the coefficient; this might end up with 1000... + ub=num.lsd; + for (; UBTOUI(ub-3)==0x09090909; ub-=4) UBFROMUI(ub-3, 0); + for (; *ub==9; ub--) *ub=0; // at most 3 more + *ub+=1; + if (ub9 + #error Exponent may overflow when doubled for Multiply +#endif +#if MULACCLEN!=(MULACCLEN/4)*4 + // This assumption is used below only for initialization + #error MULACCLEN is not a multiple of 4 +#endif + +static void decFiniteMultiply(bcdnum *num, uByte *bcdacc, + const decFloat *dfl, const decFloat *dfr) { + uInt bufl[MULOPLEN]; // left coefficient (base-billion) + uInt bufr[MULOPLEN]; // right coefficient (base-billion) + uInt *ui, *uj; // work + uByte *ub; // .. + uInt uiwork; // for macros + + #if DECUSE64 + uLong accl[MULACCLEN]; // lazy accumulator (base-billion+) + uLong *pl; // work -> lazy accumulator + uInt acc[MULACCLEN]; // coefficent in base-billion .. + #else + uInt acc[MULACCLEN*2]; // accumulator in base-billion .. + #endif + uInt *pa; // work -> accumulator + //printf("Base10**9: OpLen=%d MulAcclen=%d\n", OPLEN, MULACCLEN); + + /* Calculate sign and exponent */ + num->sign=(DFWORD(dfl, 0)^DFWORD(dfr, 0)) & DECFLOAT_Sign; + num->exponent=GETEXPUN(dfl)+GETEXPUN(dfr); // [see assertion above] + + /* Extract the coefficients and prepare the accumulator */ + // the coefficients of the operands are decoded into base-billion + // numbers in uInt arrays (bufl and bufr, LSD at offset 0) of the + // appropriate size. + GETCOEFFBILL(dfl, bufl); + GETCOEFFBILL(dfr, bufr); + #if DECTRACE && 0 + printf("CoeffbL:"); + for (ui=bufl+MULOPLEN-1; ui>=bufl; ui--) printf(" %08lx", (LI)*ui); + printf("\n"); + printf("CoeffbR:"); + for (uj=bufr+MULOPLEN-1; uj>=bufr; uj--) printf(" %08lx", (LI)*uj); + printf("\n"); + #endif + + // start the 64-bit/32-bit differing paths... +#if DECUSE64 + + // zero the accumulator + #if MULACCLEN==4 + accl[0]=0; accl[1]=0; accl[2]=0; accl[3]=0; + #else // use a loop + // MULACCLEN is a multiple of four, asserted above + for (pl=accl; pl1 may be + // needed. Values of A and B are chosen to satisfy the constraints + // just mentioned while minimizing the maximum error (and hence the + // maximum correction), as shown in the following table: + // + // Type OPLEN A B maxX maxError maxCorrection + // --------------------------------------------------------- + // DOUBLE 2 29 32 <2*10**18 0.63 1 + // QUAD 4 30 31 <4*10**18 1.17 2 + // + // In the OPLEN==2 case there is most choice, but the value for B + // of 32 has a big advantage as then the calculation of the + // estimate requires no shifting; the compiler can extract the high + // word directly after multiplying magic*hop. + #define MULMAGIC 2305843009U // 2**61/10**9 [both cases] + #if DOUBLE + #define MULSHIFTA 29 + #define MULSHIFTB 32 + #elif QUAD + #define MULSHIFTA 30 + #define MULSHIFTB 31 + #else + #error Unexpected type + #endif + + #if DECTRACE + printf("MulAccl:"); + for (pl=accl+MULACCLEN-1; pl>=accl; pl--) + printf(" %08lx:%08lx", (LI)(*pl>>32), (LI)(*pl&0xffffffff)); + printf("\n"); + #endif + + for (pl=accl, pa=acc; pl=MULTBASE) { + // *pl holds a binary number which needs to be split + hop=(uInt)(*pl>>MULSHIFTA); + est=(uInt)(((uLong)hop*MULMAGIC)>>MULSHIFTB); + // the estimate is now in est; now calculate hi:lo-est*10**9; + // happily the top word of the result is irrelevant because it + // will always be zero so this needs only one multiplication + lo=(uInt)(*pl-((uLong)est*MULTBASE)); // low word of result + // If QUAD, the correction here could be +2 + if (lo>=MULTBASE) { + lo-=MULTBASE; // correct by +1 + est++; + #if QUAD + // may need to correct by +2 + if (lo>=MULTBASE) { + lo-=MULTBASE; + est++; + } + #endif + } + // finally place lo as the new coefficient 'digit' and add est to + // the next place up [this is safe because this path is never + // taken on the final iteration as *pl will fit] + *pa=lo; + *(pl+1)+=est; + } // *pl needed split + else { // *pl1 may be + // needed. Values of A and B are chosen to satisfy the constraints + // just mentioned while minimizing the maximum error (and hence the + // maximum correction), as shown in the following table: + // + // Type OPLEN A B maxX maxError maxCorrection + // --------------------------------------------------------- + // DOUBLE 2 29 32 <2*10**18 0.63 1 + // QUAD 4 30 31 <4*10**18 1.17 2 + // + // In the OPLEN==2 case there is most choice, but the value for B + // of 32 has a big advantage as then the calculation of the + // estimate requires no shifting; the high word is simply + // calculated from multiplying magic*hop. + #define MULMAGIC 2305843009U // 2**61/10**9 [both cases] + #if DOUBLE + #define MULSHIFTA 29 + #define MULSHIFTB 32 + #elif QUAD + #define MULSHIFTA 30 + #define MULSHIFTB 31 + #else + #error Unexpected type + #endif + + #if DECTRACE + printf("MulHiLo:"); + for (pa=acc+MULACCLEN-1; pa>=acc; pa--) + printf(" %08lx:%08lx", (LI)*(pa+MULACCLEN), (LI)*pa); + printf("\n"); + #endif + + for (pa=acc;; pa++) { // each low uInt + uInt hi, lo; // words of exact multiply result + uInt hop, estlo; // work + #if QUAD + uInt esthi; // .. + #endif + + lo=*pa; + hi=*(pa+MULACCLEN); // top 32 bits + // hi and lo now hold a binary number which needs to be split + + #if DOUBLE + hop=(hi<<3)+(lo>>MULSHIFTA); // hi:lo/2**29 + LONGMUL32HI(estlo, hop, MULMAGIC);// only need the high word + // [MULSHIFTB is 32, so estlo can be used directly] + // the estimate is now in estlo; now calculate hi:lo-est*10**9; + // happily the top word of the result is irrelevant because it + // will always be zero so this needs only one multiplication + lo-=(estlo*MULTBASE); + // esthi=0; // high word is ignored below + // the correction here will be at most +1; do it + if (lo>=MULTBASE) { + lo-=MULTBASE; + estlo++; + } + #elif QUAD + hop=(hi<<2)+(lo>>MULSHIFTA); // hi:lo/2**30 + LONGMUL32HI(esthi, hop, MULMAGIC);// shift will be 31 .. + estlo=hop*MULMAGIC; // .. so low word needed + estlo=(esthi<<1)+(estlo>>MULSHIFTB); // [just the top bit] + // esthi=0; // high word is ignored below + lo-=(estlo*MULTBASE); // as above + // the correction here could be +1 or +2 + if (lo>=MULTBASE) { + lo-=MULTBASE; + estlo++; + } + if (lo>=MULTBASE) { + lo-=MULTBASE; + estlo++; + } + #else + #error Unexpected type + #endif + + // finally place lo as the new accumulator digit and add est to + // the next place up; this latter add could cause a carry of 1 + // to the high word of the next place + *pa=lo; + *(pa+1)+=estlo; + // esthi is always 0 for DOUBLE and QUAD so this is skipped + // *(pa+1+MULACCLEN)+=esthi; + if (*(pa+1)=acc; pa--) printf(" %09ld", (LI)*pa); + printf("\n"); + #endif + + // Now convert to BCD for rounding and cleanup, starting from the + // most significant end + pa=acc+MULACCLEN-1; + if (*pa!=0) num->msd=bcdacc+LEADZEROS;// drop known lead zeros + else { // >=1 word of leading zeros + num->msd=bcdacc; // known leading zeros are gone + pa--; // skip first word .. + for (; *pa==0; pa--) if (pa==acc) break; // .. and any more leading 0s + } + for (ub=bcdacc;; pa--, ub+=9) { + if (*pa!=0) { // split(s) needed + uInt top, mid, rem; // work + // *pa is non-zero -- split the base-billion acc digit into + // hi, mid, and low three-digits + #define mulsplit9 1000000 // divisor + #define mulsplit6 1000 // divisor + // The splitting is done by simple divides and remainders, + // assuming the compiler will optimize these where useful + // [GCC does] + top=*pa/mulsplit9; + rem=*pa%mulsplit9; + mid=rem/mulsplit6; + rem=rem%mulsplit6; + // lay out the nine BCD digits (plus one unwanted byte) + UBFROMUI(ub, UBTOUI(&BIN2BCD8[top*4])); + UBFROMUI(ub+3, UBTOUI(&BIN2BCD8[mid*4])); + UBFROMUI(ub+6, UBTOUI(&BIN2BCD8[rem*4])); + } + else { // *pa==0 + UBFROMUI(ub, 0); // clear 9 BCD8s + UBFROMUI(ub+4, 0); // .. + *(ub+8)=0; // .. + } + if (pa==acc) break; + } // BCD conversion loop + + num->lsd=ub+8; // complete the bcdnum .. + + #if DECTRACE + decShowNum(num, "postmult"); + decFloatShow(dfl, "dfl"); + decFloatShow(dfr, "dfr"); + #endif + return; + } // decFiniteMultiply + +/* ------------------------------------------------------------------ */ +/* decFloatAbs -- absolute value, heeding NaNs, etc. */ +/* */ +/* result gets the canonicalized df with sign 0 */ +/* df is the decFloat to abs */ +/* set is the context */ +/* returns result */ +/* */ +/* This has the same effect as decFloatPlus unless df is negative, */ +/* in which case it has the same effect as decFloatMinus. The */ +/* effect is also the same as decFloatCopyAbs except that NaNs are */ +/* handled normally (the sign of a NaN is not affected, and an sNaN */ +/* will signal) and the result will be canonical. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatAbs(decFloat *result, const decFloat *df, + decContext *set) { + if (DFISNAN(df)) return decNaNs(result, df, NULL, set); + decCanonical(result, df); // copy and check + DFBYTE(result, 0)&=~0x80; // zero sign bit + return result; + } // decFloatAbs + +/* ------------------------------------------------------------------ */ +/* decFloatAdd -- add two decFloats */ +/* */ +/* result gets the result of adding dfl and dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +#if QUAD +// Table for testing MSDs for fastpath elimination; returns the MSD of +// a decDouble or decQuad (top 6 bits tested) ignoring the sign. +// Infinities return -32 and NaNs return -128 so that summing the two +// MSDs also allows rapid tests for the Specials (see code below). +const Int DECTESTMSD[64]={ + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 9, 8, 9, -32, -128, + 0, 1, 2, 3, 4, 5, 6, 7, 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 9, 8, 9, -32, -128}; +#else +// The table for testing MSDs is shared between the modules +extern const Int DECTESTMSD[64]; +#endif + +decFloat * decFloatAdd(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + bcdnum num; // for final conversion + Int bexpl, bexpr; // left and right biased exponents + uByte *ub, *us, *ut; // work + uInt uiwork; // for macros + #if QUAD + uShort uswork; // .. + #endif + + uInt sourhil, sourhir; // top words from source decFloats + // [valid only through end of + // fastpath code -- before swap] + uInt diffsign; // non-zero if signs differ + uInt carry; // carry: 0 or 1 before add loop + Int overlap; // coefficient overlap (if full) + Int summ; // sum of the MSDs + // the following buffers hold coefficients with various alignments + // (see commentary and diagrams below) + uByte acc[4+2+DECPMAX*3+8]; + uByte buf[4+2+DECPMAX*2]; + uByte *umsd, *ulsd; // local MSD and LSD pointers + + #if DECLITEND + #define CARRYPAT 0x01000000 // carry=1 pattern + #else + #define CARRYPAT 0x00000001 // carry=1 pattern + #endif + + /* Start decoding the arguments */ + // The initial exponents are placed into the opposite Ints to + // that which might be expected; there are two sets of data to + // keep track of (each decFloat and the corresponding exponent), + // and this scheme means that at the swap point (after comparing + // exponents) only one pair of words needs to be swapped + // whichever path is taken (thereby minimising worst-case path). + // The calculated exponents will be nonsense when the arguments are + // Special, but are not used in that path + sourhil=DFWORD(dfl, 0); // LHS top word + summ=DECTESTMSD[sourhil>>26]; // get first MSD for testing + bexpr=DECCOMBEXP[sourhil>>26]; // get exponent high bits (in place) + bexpr+=GETECON(dfl); // .. + continuation + + sourhir=DFWORD(dfr, 0); // RHS top word + summ+=DECTESTMSD[sourhir>>26]; // sum MSDs for testing + bexpl=DECCOMBEXP[sourhir>>26]; + bexpl+=GETECON(dfr); + + // here bexpr has biased exponent from lhs, and vice versa + + diffsign=(sourhil^sourhir)&DECFLOAT_Sign; + + // now determine whether to take a fast path or the full-function + // slow path. The slow path must be taken when: + // -- both numbers are finite, and: + // the exponents are different, or + // the signs are different, or + // the sum of the MSDs is >8 (hence might overflow) + // specialness and the sum of the MSDs can be tested at once using + // the summ value just calculated, so the test for specials is no + // longer on the worst-case path (as of 3.60) + + if (summ<=8) { // MSD+MSD is good, or there is a special + if (summ<0) { // there is a special + // Inf+Inf would give -64; Inf+finite is -32 or higher + if (summ<-64) return decNaNs(result, dfl, dfr, set); // one or two NaNs + // two infinities with different signs is invalid + if (summ==-64 && diffsign) return decInvalid(result, set); + if (DFISINF(dfl)) return decInfinity(result, dfl); // LHS is infinite + return decInfinity(result, dfr); // RHS must be Inf + } + // Here when both arguments are finite; fast path is possible + // (currently only for aligned and same-sign) + if (bexpr==bexpl && !diffsign) { + uInt tac[DECLETS+1]; // base-1000 coefficient + uInt encode; // work + + // Get one coefficient as base-1000 and add the other + GETCOEFFTHOU(dfl, tac); // least-significant goes to [0] + ADDCOEFFTHOU(dfr, tac); + // here the sum of the MSDs (plus any carry) will be <10 due to + // the fastpath test earlier + + // construct the result; low word is the same for both formats + encode =BIN2DPD[tac[0]]; + encode|=BIN2DPD[tac[1]]<<10; + encode|=BIN2DPD[tac[2]]<<20; + encode|=BIN2DPD[tac[3]]<<30; + DFWORD(result, (DECBYTES/4)-1)=encode; + + // collect next two declets (all that remains, for Double) + encode =BIN2DPD[tac[3]]>>2; + encode|=BIN2DPD[tac[4]]<<8; + + #if QUAD + // complete and lay out middling words + encode|=BIN2DPD[tac[5]]<<18; + encode|=BIN2DPD[tac[6]]<<28; + DFWORD(result, 2)=encode; + + encode =BIN2DPD[tac[6]]>>4; + encode|=BIN2DPD[tac[7]]<<6; + encode|=BIN2DPD[tac[8]]<<16; + encode|=BIN2DPD[tac[9]]<<26; + DFWORD(result, 1)=encode; + + // and final two declets + encode =BIN2DPD[tac[9]]>>6; + encode|=BIN2DPD[tac[10]]<<4; + #endif + + // add exponent continuation and sign (from either argument) + encode|=sourhil & (ECONMASK | DECFLOAT_Sign); + + // create lookup index = MSD + top two bits of biased exponent <<4 + tac[DECLETS]|=(bexpl>>DECECONL)<<4; + encode|=DECCOMBFROM[tac[DECLETS]]; // add constructed combination field + DFWORD(result, 0)=encode; // complete + + // decFloatShow(result, ">"); + return result; + } // fast path OK + // drop through to slow path + } // low sum or Special(s) + + /* Slow path required -- arguments are finite and might overflow, */ + /* or require alignment, or might have different signs */ + + // now swap either exponents or argument pointers + if (bexpl<=bexpr) { + // original left is bigger + Int bexpswap=bexpl; + bexpl=bexpr; + bexpr=bexpswap; + // printf("left bigger\n"); + } + else { + const decFloat *dfswap=dfl; + dfl=dfr; + dfr=dfswap; + // printf("right bigger\n"); + } + // [here dfl and bexpl refer to the datum with the larger exponent, + // of if the exponents are equal then the original LHS argument] + + // if lhs is zero then result will be the rhs (now known to have + // the smaller exponent), which also may need to be tested for zero + // for the weird IEEE 754 sign rules + if (DFISZERO(dfl)) { + decCanonical(result, dfr); // clean copy + // "When the sum of two operands with opposite signs is + // exactly zero, the sign of that sum shall be '+' in all + // rounding modes except round toward -Infinity, in which + // mode that sign shall be '-'." + if (diffsign && DFISZERO(result)) { + DFWORD(result, 0)&=~DECFLOAT_Sign; // assume sign 0 + if (set->round==DEC_ROUND_FLOOR) DFWORD(result, 0)|=DECFLOAT_Sign; + } + return result; + } // numfl is zero + // [here, LHS is non-zero; code below assumes that] + + // Coefficients layout during the calculations to follow: + // + // Overlap case: + // +------------------------------------------------+ + // acc: |0000| coeffa | tail B | | + // +------------------------------------------------+ + // buf: |0000| pad0s | coeffb | | + // +------------------------------------------------+ + // + // Touching coefficients or gap: + // +------------------------------------------------+ + // acc: |0000| coeffa | gap | coeffb | + // +------------------------------------------------+ + // [buf not used or needed; gap clamped to Pmax] + + // lay out lhs coefficient into accumulator; this starts at acc+4 + // for decDouble or acc+6 for decQuad so the LSD is word- + // aligned; the top word gap is there only in case a carry digit + // is prefixed after the add -- it does not need to be zeroed + #if DOUBLE + #define COFF 4 // offset into acc + #elif QUAD + UBFROMUS(acc+4, 0); // prefix 00 + #define COFF 6 // offset into acc + #endif + + GETCOEFF(dfl, acc+COFF); // decode from decFloat + ulsd=acc+COFF+DECPMAX-1; + umsd=acc+4; // [having this here avoids + + #if DECTRACE + {bcdnum tum; + tum.msd=umsd; + tum.lsd=ulsd; + tum.exponent=bexpl-DECBIAS; + tum.sign=DFWORD(dfl, 0) & DECFLOAT_Sign; + decShowNum(&tum, "dflx");} + #endif + + // if signs differ, take ten's complement of lhs (here the + // coefficient is subtracted from all-nines; the 1 is added during + // the later add cycle -- zeros to the right do not matter because + // the complement of zero is zero); these are fixed-length inverts + // where the lsd is known to be at a 4-byte boundary (so no borrow + // possible) + carry=0; // assume no carry + if (diffsign) { + carry=CARRYPAT; // for +1 during add + UBFROMUI(acc+ 4, 0x09090909-UBTOUI(acc+ 4)); + UBFROMUI(acc+ 8, 0x09090909-UBTOUI(acc+ 8)); + UBFROMUI(acc+12, 0x09090909-UBTOUI(acc+12)); + UBFROMUI(acc+16, 0x09090909-UBTOUI(acc+16)); + #if QUAD + UBFROMUI(acc+20, 0x09090909-UBTOUI(acc+20)); + UBFROMUI(acc+24, 0x09090909-UBTOUI(acc+24)); + UBFROMUI(acc+28, 0x09090909-UBTOUI(acc+28)); + UBFROMUI(acc+32, 0x09090909-UBTOUI(acc+32)); + UBFROMUI(acc+36, 0x09090909-UBTOUI(acc+36)); + #endif + } // diffsign + + // now process the rhs coefficient; if it cannot overlap lhs then + // it can be put straight into acc (with an appropriate gap, if + // needed) because no actual addition will be needed (except + // possibly to complete ten's complement) + overlap=DECPMAX-(bexpl-bexpr); + #if DECTRACE + printf("exps: %ld %ld\n", (LI)(bexpl-DECBIAS), (LI)(bexpr-DECBIAS)); + printf("Overlap=%ld carry=%08lx\n", (LI)overlap, (LI)carry); + #endif + + if (overlap<=0) { // no overlap possible + uInt gap; // local work + // since a full addition is not needed, a ten's complement + // calculation started above may need to be completed + if (carry) { + for (ub=ulsd; *ub==9; ub--) *ub=0; + *ub+=1; + carry=0; // taken care of + } + // up to DECPMAX-1 digits of the final result can extend down + // below the LSD of the lhs, so if the gap is >DECPMAX then the + // rhs will be simply sticky bits. In this case the gap is + // clamped to DECPMAX and the exponent adjusted to suit [this is + // safe because the lhs is non-zero]. + gap=-overlap; + if (gap>DECPMAX) { + bexpr+=gap-1; + gap=DECPMAX; + } + ub=ulsd+gap+1; // where MSD will go + // Fill the gap with 0s; note that there is no addition to do + ut=acc+COFF+DECPMAX; // start of gap + for (; ut DECPMAX + *ub=(uByte)(!DFISZERO(dfr)); // make sticky digit + } + else { // need full coefficient + GETCOEFF(dfr, ub); // decode from decFloat + ub+=DECPMAX-1; // new LSD... + } + ulsd=ub; // save new LSD + } // no overlap possible + + else { // overlap>0 + // coefficients overlap (perhaps completely, although also + // perhaps only where zeros) + if (overlap==DECPMAX) { // aligned + ub=buf+COFF; // where msd will go + #if QUAD + UBFROMUS(buf+4, 0); // clear quad's 00 + #endif + GETCOEFF(dfr, ub); // decode from decFloat + } + else { // unaligned + ub=buf+COFF+DECPMAX-overlap; // where MSD will go + // Fill the prefix gap with 0s; 8 will cover most common + // unalignments, so start with direct assignments (a loop is + // then used for any remaining -- the loop (and the one in a + // moment) is not then on the critical path because the number + // of additions is reduced by (at least) two in this case) + UBFROMUI(buf+4, 0); // [clears decQuad 00 too] + UBFROMUI(buf+8, 0); + if (ub>buf+12) { + ut=buf+12; // start any remaining + for (; ut=acc+4; ut-=4, us-=4) { // big-endian add loop + // bcd8 add + carry+=UBTOUI(us); // rhs + carry + if (carry==0) continue; // no-op + carry+=UBTOUI(ut); // lhs + // Big-endian BCD adjust (uses internal carry) + carry+=0x76f6f6f6; // note top nibble not all bits + // apply BCD adjust and save + UBFROMUI(ut, (carry & 0x0f0f0f0f) - ((carry & 0x60606060)>>4)); + carry>>=31; // true carry was at far left + } // add loop + #else + for (; ut>=acc+4; ut-=4, us-=4) { // little-endian add loop + // bcd8 add + carry+=UBTOUI(us); // rhs + carry + if (carry==0) continue; // no-op [common if unaligned] + carry+=UBTOUI(ut); // lhs + // Little-endian BCD adjust; inter-digit carry must be manual + // because the lsb from the array will be in the most-significant + // byte of carry + carry+=0x76767676; // note no inter-byte carries + carry+=(carry & 0x80000000)>>15; + carry+=(carry & 0x00800000)>>15; + carry+=(carry & 0x00008000)>>15; + carry-=(carry & 0x60606060)>>4; // BCD adjust back + UBFROMUI(ut, carry & 0x0f0f0f0f); // clear debris and save + // here, final carry-out bit is at 0x00000080; move it ready + // for next word-add (i.e., to 0x01000000) + carry=(carry & 0x00000080)<<17; + } // add loop + #endif + + #if DECTRACE + {bcdnum tum; + printf("Add done, carry=%08lx, diffsign=%ld\n", (LI)carry, (LI)diffsign); + tum.msd=umsd; // acc+4; + tum.lsd=ulsd; + tum.exponent=0; + tum.sign=0; + decShowNum(&tum, "dfadd");} + #endif + } // overlap possible + + // ordering here is a little strange in order to have slowest path + // first in GCC asm listing + if (diffsign) { // subtraction + if (!carry) { // no carry out means RHS=umsd+BNEXT) { // unaligned + // eight will handle most unaligments for Double; 16 for Quad + UBFROMUI(umsd+BNEXT, 0x09090909-UBTOUI(umsd+BNEXT)); + UBFROMUI(umsd+BNEXT+4, 0x09090909-UBTOUI(umsd+BNEXT+4)); + #if DOUBLE + #define BNEXTY (BNEXT+8) + #elif QUAD + UBFROMUI(umsd+BNEXT+8, 0x09090909-UBTOUI(umsd+BNEXT+8)); + UBFROMUI(umsd+BNEXT+12, 0x09090909-UBTOUI(umsd+BNEXT+12)); + #define BNEXTY (BNEXT+16) + #endif + if (ulsd>=umsd+BNEXTY) { // very unaligned + ut=umsd+BNEXTY; // -> continue + for (;;ut+=4) { + UBFROMUI(ut, 0x09090909-UBTOUI(ut)); // invert four digits + if (ut>=ulsd-3) break; // all done + } + } + } + // complete the ten's complement by adding 1 + for (ub=ulsd; *ub==9; ub--) *ub=0; + *ub+=1; + } // borrowed + + else { // carry out means RHS>=LHS + num.sign=DFWORD(dfr, 0) & DECFLOAT_Sign; + // all done except for the special IEEE 754 exact-zero-result + // rule (see above); while testing for zero, strip leading + // zeros (which will save decFinalize doing it) (this is in + // diffsign path, so carry impossible and true umsd is + // acc+COFF) + + // Check the initial coefficient area using the fast macro; + // this will often be all that needs to be done (as on the + // worst-case path when the subtraction was aligned and + // full-length) + if (ISCOEFFZERO(acc+COFF)) { + umsd=acc+COFF+DECPMAX-1; // so far, so zero + if (ulsd>umsd) { // more to check + umsd++; // to align after checked area + for (; UBTOUI(umsd)==0 && umsd+3round==DEC_ROUND_FLOOR) num.sign=DECFLOAT_Sign; + } + } + // [else was not zero, might still have leading zeros] + } // subtraction gave positive result + } // diffsign + + else { // same-sign addition + num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign; + #if DOUBLE + if (carry) { // only possible with decDouble + *(acc+3)=1; // [Quad has leading 00] + umsd=acc+3; + } + #endif + } // same sign + + num.msd=umsd; // set MSD .. + num.lsd=ulsd; // .. and LSD + num.exponent=bexpr-DECBIAS; // set exponent to smaller, unbiassed + + #if DECTRACE + decFloatShow(dfl, "dfl"); + decFloatShow(dfr, "dfr"); + decShowNum(&num, "postadd"); + #endif + return decFinalize(result, &num, set); // round, check, and lay out + } // decFloatAdd + +/* ------------------------------------------------------------------ */ +/* decFloatAnd -- logical digitwise AND of two decFloats */ +/* */ +/* result gets the result of ANDing dfl and dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result, which will be canonical with sign=0 */ +/* */ +/* The operands must be positive, finite with exponent q=0, and */ +/* comprise just zeros and ones; if not, Invalid operation results. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatAnd(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + if (!DFISUINT01(dfl) || !DFISUINT01(dfr) + || !DFISCC01(dfl) || !DFISCC01(dfr)) return decInvalid(result, set); + // the operands are positive finite integers (q=0) with just 0s and 1s + #if DOUBLE + DFWORD(result, 0)=ZEROWORD + |((DFWORD(dfl, 0) & DFWORD(dfr, 0))&0x04009124); + DFWORD(result, 1)=(DFWORD(dfl, 1) & DFWORD(dfr, 1))&0x49124491; + #elif QUAD + DFWORD(result, 0)=ZEROWORD + |((DFWORD(dfl, 0) & DFWORD(dfr, 0))&0x04000912); + DFWORD(result, 1)=(DFWORD(dfl, 1) & DFWORD(dfr, 1))&0x44912449; + DFWORD(result, 2)=(DFWORD(dfl, 2) & DFWORD(dfr, 2))&0x12449124; + DFWORD(result, 3)=(DFWORD(dfl, 3) & DFWORD(dfr, 3))&0x49124491; + #endif + return result; + } // decFloatAnd + +/* ------------------------------------------------------------------ */ +/* decFloatCanonical -- copy a decFloat, making canonical */ +/* */ +/* result gets the canonicalized df */ +/* df is the decFloat to copy and make canonical */ +/* returns result */ +/* */ +/* This works on specials, too; no error or exception is possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCanonical(decFloat *result, const decFloat *df) { + return decCanonical(result, df); + } // decFloatCanonical + +/* ------------------------------------------------------------------ */ +/* decFloatClass -- return the class of a decFloat */ +/* */ +/* df is the decFloat to test */ +/* returns the decClass that df falls into */ +/* ------------------------------------------------------------------ */ +enum decClass decFloatClass(const decFloat *df) { + Int exp; // exponent + if (DFISSPECIAL(df)) { + if (DFISQNAN(df)) return DEC_CLASS_QNAN; + if (DFISSNAN(df)) return DEC_CLASS_SNAN; + // must be an infinity + if (DFISSIGNED(df)) return DEC_CLASS_NEG_INF; + return DEC_CLASS_POS_INF; + } + if (DFISZERO(df)) { // quite common + if (DFISSIGNED(df)) return DEC_CLASS_NEG_ZERO; + return DEC_CLASS_POS_ZERO; + } + // is finite and non-zero; similar code to decFloatIsNormal, here + // [this could be speeded up slightly by in-lining decFloatDigits] + exp=GETEXPUN(df) // get unbiased exponent .. + +decFloatDigits(df)-1; // .. and make adjusted exponent + if (exp>=DECEMIN) { // is normal + if (DFISSIGNED(df)) return DEC_CLASS_NEG_NORMAL; + return DEC_CLASS_POS_NORMAL; + } + // is subnormal + if (DFISSIGNED(df)) return DEC_CLASS_NEG_SUBNORMAL; + return DEC_CLASS_POS_SUBNORMAL; + } // decFloatClass + +/* ------------------------------------------------------------------ */ +/* decFloatClassString -- return the class of a decFloat as a string */ +/* */ +/* df is the decFloat to test */ +/* returns a constant string describing the class df falls into */ +/* ------------------------------------------------------------------ */ +const char *decFloatClassString(const decFloat *df) { + enum decClass eclass=decFloatClass(df); + if (eclass==DEC_CLASS_POS_NORMAL) return DEC_ClassString_PN; + if (eclass==DEC_CLASS_NEG_NORMAL) return DEC_ClassString_NN; + if (eclass==DEC_CLASS_POS_ZERO) return DEC_ClassString_PZ; + if (eclass==DEC_CLASS_NEG_ZERO) return DEC_ClassString_NZ; + if (eclass==DEC_CLASS_POS_SUBNORMAL) return DEC_ClassString_PS; + if (eclass==DEC_CLASS_NEG_SUBNORMAL) return DEC_ClassString_NS; + if (eclass==DEC_CLASS_POS_INF) return DEC_ClassString_PI; + if (eclass==DEC_CLASS_NEG_INF) return DEC_ClassString_NI; + if (eclass==DEC_CLASS_QNAN) return DEC_ClassString_QN; + if (eclass==DEC_CLASS_SNAN) return DEC_ClassString_SN; + return DEC_ClassString_UN; // Unknown + } // decFloatClassString + +/* ------------------------------------------------------------------ */ +/* decFloatCompare -- compare two decFloats; quiet NaNs allowed */ +/* */ +/* result gets the result of comparing dfl and dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result, which may be -1, 0, 1, or NaN (Unordered) */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCompare(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int comp; // work + // NaNs are handled as usual + if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + // numeric comparison needed + comp=decNumCompare(dfl, dfr, 0); + decFloatZero(result); + if (comp==0) return result; + DFBYTE(result, DECBYTES-1)=0x01; // LSD=1 + if (comp<0) DFBYTE(result, 0)|=0x80; // set sign bit + return result; + } // decFloatCompare + +/* ------------------------------------------------------------------ */ +/* decFloatCompareSignal -- compare two decFloats; all NaNs signal */ +/* */ +/* result gets the result of comparing dfl and dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result, which may be -1, 0, 1, or NaN (Unordered) */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCompareSignal(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int comp; // work + // NaNs are handled as usual, except that all NaNs signal + if (DFISNAN(dfl) || DFISNAN(dfr)) { + set->status|=DEC_Invalid_operation; + return decNaNs(result, dfl, dfr, set); + } + // numeric comparison needed + comp=decNumCompare(dfl, dfr, 0); + decFloatZero(result); + if (comp==0) return result; + DFBYTE(result, DECBYTES-1)=0x01; // LSD=1 + if (comp<0) DFBYTE(result, 0)|=0x80; // set sign bit + return result; + } // decFloatCompareSignal + +/* ------------------------------------------------------------------ */ +/* decFloatCompareTotal -- compare two decFloats with total ordering */ +/* */ +/* result gets the result of comparing dfl and dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* returns result, which may be -1, 0, or 1 */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCompareTotal(decFloat *result, + const decFloat *dfl, const decFloat *dfr) { + Int comp; // work + uInt uiwork; // for macros + #if QUAD + uShort uswork; // .. + #endif + if (DFISNAN(dfl) || DFISNAN(dfr)) { + Int nanl, nanr; // work + // morph NaNs to +/- 1 or 2, leave numbers as 0 + nanl=DFISSNAN(dfl)+DFISQNAN(dfl)*2; // quiet > signalling + if (DFISSIGNED(dfl)) nanl=-nanl; + nanr=DFISSNAN(dfr)+DFISQNAN(dfr)*2; + if (DFISSIGNED(dfr)) nanr=-nanr; + if (nanl>nanr) comp=+1; + else if (nanl*uc) comp=sigl; // difference found + else comp=-sigl; // .. + break; + } + } + } // same NaN type and sign + } + else { + // numeric comparison needed + comp=decNumCompare(dfl, dfr, 1); // total ordering + } + decFloatZero(result); + if (comp==0) return result; + DFBYTE(result, DECBYTES-1)=0x01; // LSD=1 + if (comp<0) DFBYTE(result, 0)|=0x80; // set sign bit + return result; + } // decFloatCompareTotal + +/* ------------------------------------------------------------------ */ +/* decFloatCompareTotalMag -- compare magnitudes with total ordering */ +/* */ +/* result gets the result of comparing abs(dfl) and abs(dfr) */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* returns result, which may be -1, 0, or 1 */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCompareTotalMag(decFloat *result, + const decFloat *dfl, const decFloat *dfr) { + decFloat a, b; // for copy if needed + // copy and redirect signed operand(s) + if (DFISSIGNED(dfl)) { + decFloatCopyAbs(&a, dfl); + dfl=&a; + } + if (DFISSIGNED(dfr)) { + decFloatCopyAbs(&b, dfr); + dfr=&b; + } + return decFloatCompareTotal(result, dfl, dfr); + } // decFloatCompareTotalMag + +/* ------------------------------------------------------------------ */ +/* decFloatCopy -- copy a decFloat as-is */ +/* */ +/* result gets the copy of dfl */ +/* dfl is the decFloat to copy */ +/* returns result */ +/* */ +/* This is a bitwise operation; no errors or exceptions are possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCopy(decFloat *result, const decFloat *dfl) { + if (dfl!=result) *result=*dfl; // copy needed + return result; + } // decFloatCopy + +/* ------------------------------------------------------------------ */ +/* decFloatCopyAbs -- copy a decFloat as-is and set sign bit to 0 */ +/* */ +/* result gets the copy of dfl with sign bit 0 */ +/* dfl is the decFloat to copy */ +/* returns result */ +/* */ +/* This is a bitwise operation; no errors or exceptions are possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCopyAbs(decFloat *result, const decFloat *dfl) { + if (dfl!=result) *result=*dfl; // copy needed + DFBYTE(result, 0)&=~0x80; // zero sign bit + return result; + } // decFloatCopyAbs + +/* ------------------------------------------------------------------ */ +/* decFloatCopyNegate -- copy a decFloat as-is with inverted sign bit */ +/* */ +/* result gets the copy of dfl with sign bit inverted */ +/* dfl is the decFloat to copy */ +/* returns result */ +/* */ +/* This is a bitwise operation; no errors or exceptions are possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCopyNegate(decFloat *result, const decFloat *dfl) { + if (dfl!=result) *result=*dfl; // copy needed + DFBYTE(result, 0)^=0x80; // invert sign bit + return result; + } // decFloatCopyNegate + +/* ------------------------------------------------------------------ */ +/* decFloatCopySign -- copy a decFloat with the sign of another */ +/* */ +/* result gets the result of copying dfl with the sign of dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* returns result */ +/* */ +/* This is a bitwise operation; no errors or exceptions are possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatCopySign(decFloat *result, + const decFloat *dfl, const decFloat *dfr) { + uByte sign=(uByte)(DFBYTE(dfr, 0)&0x80); // save sign bit + if (dfl!=result) *result=*dfl; // copy needed + DFBYTE(result, 0)&=~0x80; // clear sign .. + DFBYTE(result, 0)=(uByte)(DFBYTE(result, 0)|sign); // .. and set saved + return result; + } // decFloatCopySign + +/* ------------------------------------------------------------------ */ +/* decFloatDigits -- return the number of digits in a decFloat */ +/* */ +/* df is the decFloat to investigate */ +/* returns the number of significant digits in the decFloat; a */ +/* zero coefficient returns 1 as does an infinity (a NaN returns */ +/* the number of digits in the payload) */ +/* ------------------------------------------------------------------ */ +// private macro to extract a declet according to provided formula +// (form), and if it is non-zero then return the calculated digits +// depending on the declet number (n), where n=0 for the most +// significant declet; uses uInt dpd for work +#define dpdlenchk(n, form) dpd=(form)&0x3ff; \ + if (dpd) return (DECPMAX-1-3*(n))-(3-DPD2BCD8[dpd*4+3]) +// next one is used when it is known that the declet must be +// non-zero, or is the final zero declet +#define dpdlendun(n, form) dpd=(form)&0x3ff; \ + if (dpd==0) return 1; \ + return (DECPMAX-1-3*(n))-(3-DPD2BCD8[dpd*4+3]) + +uInt decFloatDigits(const decFloat *df) { + uInt dpd; // work + uInt sourhi=DFWORD(df, 0); // top word from source decFloat + #if QUAD + uInt sourmh, sourml; + #endif + uInt sourlo; + + if (DFISINF(df)) return 1; + // A NaN effectively has an MSD of 0; otherwise if non-zero MSD + // then the coefficient is full-length + if (!DFISNAN(df) && DECCOMBMSD[sourhi>>26]) return DECPMAX; + + #if DOUBLE + if (sourhi&0x0003ffff) { // ends in first + dpdlenchk(0, sourhi>>8); + sourlo=DFWORD(df, 1); + dpdlendun(1, (sourhi<<2) | (sourlo>>30)); + } // [cannot drop through] + sourlo=DFWORD(df, 1); // sourhi not involved now + if (sourlo&0xfff00000) { // in one of first two + dpdlenchk(1, sourlo>>30); // very rare + dpdlendun(2, sourlo>>20); + } // [cannot drop through] + dpdlenchk(3, sourlo>>10); + dpdlendun(4, sourlo); + // [cannot drop through] + + #elif QUAD + if (sourhi&0x00003fff) { // ends in first + dpdlenchk(0, sourhi>>4); + sourmh=DFWORD(df, 1); + dpdlendun(1, ((sourhi)<<6) | (sourmh>>26)); + } // [cannot drop through] + sourmh=DFWORD(df, 1); + if (sourmh) { + dpdlenchk(1, sourmh>>26); + dpdlenchk(2, sourmh>>16); + dpdlenchk(3, sourmh>>6); + sourml=DFWORD(df, 2); + dpdlendun(4, ((sourmh)<<4) | (sourml>>28)); + } // [cannot drop through] + sourml=DFWORD(df, 2); + if (sourml) { + dpdlenchk(4, sourml>>28); + dpdlenchk(5, sourml>>18); + dpdlenchk(6, sourml>>8); + sourlo=DFWORD(df, 3); + dpdlendun(7, ((sourml)<<2) | (sourlo>>30)); + } // [cannot drop through] + sourlo=DFWORD(df, 3); + if (sourlo&0xfff00000) { // in one of first two + dpdlenchk(7, sourlo>>30); // very rare + dpdlendun(8, sourlo>>20); + } // [cannot drop through] + dpdlenchk(9, sourlo>>10); + dpdlendun(10, sourlo); + // [cannot drop through] + #endif + } // decFloatDigits + +/* ------------------------------------------------------------------ */ +/* decFloatDivide -- divide a decFloat by another */ +/* */ +/* result gets the result of dividing dfl by dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +// This is just a wrapper. +decFloat * decFloatDivide(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + return decDivide(result, dfl, dfr, set, DIVIDE); + } // decFloatDivide + +/* ------------------------------------------------------------------ */ +/* decFloatDivideInteger -- integer divide a decFloat by another */ +/* */ +/* result gets the result of dividing dfl by dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatDivideInteger(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + return decDivide(result, dfl, dfr, set, DIVIDEINT); + } // decFloatDivideInteger + +/* ------------------------------------------------------------------ */ +/* decFloatFMA -- multiply and add three decFloats, fused */ +/* */ +/* result gets the result of (dfl*dfr)+dff with a single rounding */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* dff is the final decFloat (fhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatFMA(decFloat *result, const decFloat *dfl, + const decFloat *dfr, const decFloat *dff, + decContext *set) { + + // The accumulator has the bytes needed for FiniteMultiply, plus + // one byte to the left in case of carry, plus DECPMAX+2 to the + // right for the final addition (up to full fhs + round & sticky) + #define FMALEN (ROUNDUP4(1+ (DECPMAX9*18+1) +DECPMAX+2)) + uByte acc[FMALEN]; // for multiplied coefficient in BCD + // .. and for final result + bcdnum mul; // for multiplication result + bcdnum fin; // for final operand, expanded + uByte coe[ROUNDUP4(DECPMAX)]; // dff coefficient in BCD + bcdnum *hi, *lo; // bcdnum with higher/lower exponent + uInt diffsign; // non-zero if signs differ + uInt hipad; // pad digit for hi if needed + Int padding; // excess exponent + uInt carry; // +1 for ten's complement and during add + uByte *ub, *uh, *ul; // work + uInt uiwork; // for macros + + // handle all the special values [any special operand leads to a + // special result] + if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr) || DFISSPECIAL(dff)) { + decFloat proxy; // multiplication result proxy + // NaNs are handled as usual, giving priority to sNaNs + if (DFISSNAN(dfl) || DFISSNAN(dfr)) return decNaNs(result, dfl, dfr, set); + if (DFISSNAN(dff)) return decNaNs(result, dff, NULL, set); + if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + if (DFISNAN(dff)) return decNaNs(result, dff, NULL, set); + // One or more of the three is infinite + // infinity times zero is bad + decFloatZero(&proxy); + if (DFISINF(dfl)) { + if (DFISZERO(dfr)) return decInvalid(result, set); + decInfinity(&proxy, &proxy); + } + else if (DFISINF(dfr)) { + if (DFISZERO(dfl)) return decInvalid(result, set); + decInfinity(&proxy, &proxy); + } + // compute sign of multiplication and place in proxy + DFWORD(&proxy, 0)|=(DFWORD(dfl, 0)^DFWORD(dfr, 0))&DECFLOAT_Sign; + if (!DFISINF(dff)) return decFloatCopy(result, &proxy); + // dff is Infinite + if (!DFISINF(&proxy)) return decInfinity(result, dff); + // both sides of addition are infinite; different sign is bad + if ((DFWORD(dff, 0)&DECFLOAT_Sign)!=(DFWORD(&proxy, 0)&DECFLOAT_Sign)) + return decInvalid(result, set); + return decFloatCopy(result, &proxy); + } + + /* Here when all operands are finite */ + + // First multiply dfl*dfr + decFiniteMultiply(&mul, acc+1, dfl, dfr); + // The multiply is complete, exact and unbounded, and described in + // mul with the coefficient held in acc[1...] + + // now add in dff; the algorithm is essentially the same as + // decFloatAdd, but the code is different because the code there + // is highly optimized for adding two numbers of the same size + fin.exponent=GETEXPUN(dff); // get dff exponent and sign + fin.sign=DFWORD(dff, 0)&DECFLOAT_Sign; + diffsign=mul.sign^fin.sign; // note if signs differ + fin.msd=coe; + fin.lsd=coe+DECPMAX-1; + GETCOEFF(dff, coe); // extract the coefficient + + // now set hi and lo so that hi points to whichever of mul and fin + // has the higher exponent and lo points to the other [don't care, + // if the same]. One coefficient will be in acc, the other in coe. + if (mul.exponent>=fin.exponent) { + hi=&mul; + lo=&fin; + } + else { + hi=&fin; + lo=&mul; + } + + // remove leading zeros on both operands; this will save time later + // and make testing for zero trivial (tests are safe because acc + // and coe are rounded up to uInts) + for (; UBTOUI(hi->msd)==0 && hi->msd+3lsd;) hi->msd+=4; + for (; *hi->msd==0 && hi->msdlsd;) hi->msd++; + for (; UBTOUI(lo->msd)==0 && lo->msd+3lsd;) lo->msd+=4; + for (; *lo->msd==0 && lo->msdlsd;) lo->msd++; + + // if hi is zero then result will be lo (which has the smaller + // exponent), which also may need to be tested for zero for the + // weird IEEE 754 sign rules + if (*hi->msd==0) { // hi is zero + // "When the sum of two operands with opposite signs is + // exactly zero, the sign of that sum shall be '+' in all + // rounding modes except round toward -Infinity, in which + // mode that sign shall be '-'." + if (diffsign) { + if (*lo->msd==0) { // lo is zero + lo->sign=0; + if (set->round==DEC_ROUND_FLOOR) lo->sign=DECFLOAT_Sign; + } // diffsign && lo=0 + } // diffsign + return decFinalize(result, lo, set); // may need clamping + } // numfl is zero + // [here, both are minimal length and hi is non-zero] + // (if lo is zero then padding with zeros may be needed, below) + + // if signs differ, take the ten's complement of hi (zeros to the + // right do not matter because the complement of zero is zero); the + // +1 is done later, as part of the addition, inserted at the + // correct digit + hipad=0; + carry=0; + if (diffsign) { + hipad=9; + carry=1; + // exactly the correct number of digits must be inverted + for (uh=hi->msd; uhlsd-3; uh+=4) UBFROMUI(uh, 0x09090909-UBTOUI(uh)); + for (; uh<=hi->lsd; uh++) *uh=(uByte)(0x09-*uh); + } + + // ready to add; note that hi has no leading zeros so gap + // calculation does not have to be as pessimistic as in decFloatAdd + // (this is much more like the arbitrary-precision algorithm in + // Rexx and decNumber) + + // padding is the number of zeros that would need to be added to hi + // for its lsd to be aligned with the lsd of lo + padding=hi->exponent-lo->exponent; + // printf("FMA pad %ld\n", (LI)padding); + + // the result of the addition will be built into the accumulator, + // starting from the far right; this could be either hi or lo, and + // will be aligned + ub=acc+FMALEN-1; // where lsd of result will go + ul=lo->lsd; // lsd of rhs + + if (padding!=0) { // unaligned + // if the msd of lo is more than DECPMAX+2 digits to the right of + // the original msd of hi then it can be reduced to a single + // digit at the right place, as it stays clear of hi digits + // [it must be DECPMAX+2 because during a subtraction the msd + // could become 0 after a borrow from 1.000 to 0.9999...] + + Int hilen=(Int)(hi->lsd-hi->msd+1); // length of hi + Int lolen=(Int)(lo->lsd-lo->msd+1); // and of lo + + if (hilen+padding-lolen > DECPMAX+2) { // can reduce lo to single + // make sure it is virtually at least DECPMAX from hi->msd, at + // least to right of hi->lsd (in case of destructive subtract), + // and separated by at least two digits from either of those + // (the tricky DOUBLE case is when hi is a 1 that will become a + // 0.9999... by subtraction: + // hi: 1 E+16 + // lo: .................1000000000000000 E-16 + // which for the addition pads to: + // hi: 1000000000000000000 E-16 + // lo: .................1000000000000000 E-16 + Int newexp=MINI(hi->exponent, hi->exponent+hilen-DECPMAX)-3; + + // printf("FMA reduce: %ld\n", (LI)reduce); + lo->lsd=lo->msd; // to single digit [maybe 0] + lo->exponent=newexp; // new lowest exponent + padding=hi->exponent-lo->exponent; // recalculate + ul=lo->lsd; // .. and repoint + } + + // padding is still > 0, but will fit in acc (less leading carry slot) + #if DECCHECK + if (padding<=0) printf("FMA low padding: %ld\n", (LI)padding); + if (hilen+padding+1>FMALEN) + printf("FMA excess hilen+padding: %ld+%ld \n", (LI)hilen, (LI)padding); + // printf("FMA padding: %ld\n", (LI)padding); + #endif + + // padding digits can now be set in the result; one or more of + // these will come from lo; others will be zeros in the gap + for (; ul-3>=lo->msd && padding>3; padding-=4, ul-=4, ub-=4) { + UBFROMUI(ub-3, UBTOUI(ul-3)); // [cannot overlap] + } + for (; ul>=lo->msd && padding>0; padding--, ul--, ub--) *ub=*ul; + for (;padding>0; padding--, ub--) *ub=0; // mind the gap + } + + // addition now complete to the right of the rightmost digit of hi + uh=hi->lsd; + + // dow do the add from hi->lsd to the left + // [bytewise, because either operand can run out at any time] + // carry was set up depending on ten's complement above + // first assume both operands have some digits + for (;; ub--) { + if (uhmsd || ulmsd) break; + *ub=(uByte)(carry+(*uh--)+(*ul--)); + carry=0; + if (*ub<10) continue; + *ub-=10; + carry=1; + } // both loop + + if (ulmsd) { // to left of lo + for (;; ub--) { + if (uhmsd) break; + *ub=(uByte)(carry+(*uh--)); // [+0] + carry=0; + if (*ub<10) continue; + *ub-=10; + carry=1; + } // hi loop + } + else { // to left of hi + for (;; ub--) { + if (ulmsd) break; + *ub=(uByte)(carry+hipad+(*ul--)); + carry=0; + if (*ub<10) continue; + *ub-=10; + carry=1; + } // lo loop + } + + // addition complete -- now handle carry, borrow, etc. + // use lo to set up the num (its exponent is already correct, and + // sign usually is) + lo->msd=ub+1; + lo->lsd=acc+FMALEN-1; + // decShowNum(lo, "lo"); + if (!diffsign) { // same-sign addition + if (carry) { // carry out + *ub=1; // place the 1 .. + lo->msd--; // .. and update + } + } // same sign + else { // signs differed (subtraction) + if (!carry) { // no carry out means hisign=hi->sign; // sign is lhs sign + for (ul=lo->msd; ullsd-3; ul+=4) UBFROMUI(ul, 0x09090909-UBTOUI(ul)); + for (; ul<=lo->lsd; ul++) *ul=(uByte)(0x09-*ul); // [leaves ul at lsd+1] + // complete the ten's complement by adding 1 [cannot overrun] + for (ul--; *ul==9; ul--) *ul=0; + *ul+=1; + } // borrowed + else { // carry out means hi>=lo + // sign to use is lo->sign + // all done except for the special IEEE 754 exact-zero-result + // rule (see above); while testing for zero, strip leading + // zeros (which will save decFinalize doing it) + for (; UBTOUI(lo->msd)==0 && lo->msd+3lsd;) lo->msd+=4; + for (; *lo->msd==0 && lo->msdlsd;) lo->msd++; + if (*lo->msd==0) { // must be true zero (and diffsign) + lo->sign=0; // assume + + if (set->round==DEC_ROUND_FLOOR) lo->sign=DECFLOAT_Sign; + } + // [else was not zero, might still have leading zeros] + } // subtraction gave positive result + } // diffsign + + #if DECCHECK + // assert no left underrun + if (lo->msdmsd)); + } + #endif + + return decFinalize(result, lo, set); // round, check, and lay out + } // decFloatFMA + +/* ------------------------------------------------------------------ */ +/* decFloatFromInt -- initialise a decFloat from an Int */ +/* */ +/* result gets the converted Int */ +/* n is the Int to convert */ +/* returns result */ +/* */ +/* The result is Exact; no errors or exceptions are possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatFromInt32(decFloat *result, Int n) { + uInt u=(uInt)n; // copy as bits + uInt encode; // work + DFWORD(result, 0)=ZEROWORD; // always + #if QUAD + DFWORD(result, 1)=0; + DFWORD(result, 2)=0; + #endif + if (n<0) { // handle -n with care + // [This can be done without the test, but is then slightly slower] + u=(~u)+1; + DFWORD(result, 0)|=DECFLOAT_Sign; + } + // Since the maximum value of u now is 2**31, only the low word of + // result is affected + encode=BIN2DPD[u%1000]; + u/=1000; + encode|=BIN2DPD[u%1000]<<10; + u/=1000; + encode|=BIN2DPD[u%1000]<<20; + u/=1000; // now 0, 1, or 2 + encode|=u<<30; + DFWORD(result, DECWORDS-1)=encode; + return result; + } // decFloatFromInt32 + +/* ------------------------------------------------------------------ */ +/* decFloatFromUInt -- initialise a decFloat from a uInt */ +/* */ +/* result gets the converted uInt */ +/* n is the uInt to convert */ +/* returns result */ +/* */ +/* The result is Exact; no errors or exceptions are possible. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatFromUInt32(decFloat *result, uInt u) { + uInt encode; // work + DFWORD(result, 0)=ZEROWORD; // always + #if QUAD + DFWORD(result, 1)=0; + DFWORD(result, 2)=0; + #endif + encode=BIN2DPD[u%1000]; + u/=1000; + encode|=BIN2DPD[u%1000]<<10; + u/=1000; + encode|=BIN2DPD[u%1000]<<20; + u/=1000; // now 0 -> 4 + encode|=u<<30; + DFWORD(result, DECWORDS-1)=encode; + DFWORD(result, DECWORDS-2)|=u>>2; // rarely non-zero + return result; + } // decFloatFromUInt32 + +/* ------------------------------------------------------------------ */ +/* decFloatInvert -- logical digitwise INVERT of a decFloat */ +/* */ +/* result gets the result of INVERTing df */ +/* df is the decFloat to invert */ +/* set is the context */ +/* returns result, which will be canonical with sign=0 */ +/* */ +/* The operand must be positive, finite with exponent q=0, and */ +/* comprise just zeros and ones; if not, Invalid operation results. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatInvert(decFloat *result, const decFloat *df, + decContext *set) { + uInt sourhi=DFWORD(df, 0); // top word of dfs + + if (!DFISUINT01(df) || !DFISCC01(df)) return decInvalid(result, set); + // the operand is a finite integer (q=0) + #if DOUBLE + DFWORD(result, 0)=ZEROWORD|((~sourhi)&0x04009124); + DFWORD(result, 1)=(~DFWORD(df, 1)) &0x49124491; + #elif QUAD + DFWORD(result, 0)=ZEROWORD|((~sourhi)&0x04000912); + DFWORD(result, 1)=(~DFWORD(df, 1)) &0x44912449; + DFWORD(result, 2)=(~DFWORD(df, 2)) &0x12449124; + DFWORD(result, 3)=(~DFWORD(df, 3)) &0x49124491; + #endif + return result; + } // decFloatInvert + +/* ------------------------------------------------------------------ */ +/* decFloatIs -- decFloat tests (IsSigned, etc.) */ +/* */ +/* df is the decFloat to test */ +/* returns 0 or 1 in a uInt */ +/* */ +/* Many of these could be macros, but having them as real functions */ +/* is a little cleaner (and they can be referred to here by the */ +/* generic names) */ +/* ------------------------------------------------------------------ */ +uInt decFloatIsCanonical(const decFloat *df) { + if (DFISSPECIAL(df)) { + if (DFISINF(df)) { + if (DFWORD(df, 0)&ECONMASK) return 0; // exponent continuation + if (!DFISCCZERO(df)) return 0; // coefficient continuation + return 1; + } + // is a NaN + if (DFWORD(df, 0)&ECONNANMASK) return 0; // exponent continuation + if (DFISCCZERO(df)) return 1; // coefficient continuation + // drop through to check payload + } + { // declare block + #if DOUBLE + uInt sourhi=DFWORD(df, 0); + uInt sourlo=DFWORD(df, 1); + if (CANONDPDOFF(sourhi, 8) + && CANONDPDTWO(sourhi, sourlo, 30) + && CANONDPDOFF(sourlo, 20) + && CANONDPDOFF(sourlo, 10) + && CANONDPDOFF(sourlo, 0)) return 1; + #elif QUAD + uInt sourhi=DFWORD(df, 0); + uInt sourmh=DFWORD(df, 1); + uInt sourml=DFWORD(df, 2); + uInt sourlo=DFWORD(df, 3); + if (CANONDPDOFF(sourhi, 4) + && CANONDPDTWO(sourhi, sourmh, 26) + && CANONDPDOFF(sourmh, 16) + && CANONDPDOFF(sourmh, 6) + && CANONDPDTWO(sourmh, sourml, 28) + && CANONDPDOFF(sourml, 18) + && CANONDPDOFF(sourml, 8) + && CANONDPDTWO(sourml, sourlo, 30) + && CANONDPDOFF(sourlo, 20) + && CANONDPDOFF(sourlo, 10) + && CANONDPDOFF(sourlo, 0)) return 1; + #endif + } // block + return 0; // a declet is non-canonical + } + +uInt decFloatIsFinite(const decFloat *df) { + return !DFISSPECIAL(df); + } +uInt decFloatIsInfinite(const decFloat *df) { + return DFISINF(df); + } +uInt decFloatIsInteger(const decFloat *df) { + return DFISINT(df); + } +uInt decFloatIsLogical(const decFloat *df) { + return DFISUINT01(df) & DFISCC01(df); + } +uInt decFloatIsNaN(const decFloat *df) { + return DFISNAN(df); + } +uInt decFloatIsNegative(const decFloat *df) { + return DFISSIGNED(df) && !DFISZERO(df) && !DFISNAN(df); + } +uInt decFloatIsNormal(const decFloat *df) { + Int exp; // exponent + if (DFISSPECIAL(df)) return 0; + if (DFISZERO(df)) return 0; + // is finite and non-zero + exp=GETEXPUN(df) // get unbiased exponent .. + +decFloatDigits(df)-1; // .. and make adjusted exponent + return (exp>=DECEMIN); // < DECEMIN is subnormal + } +uInt decFloatIsPositive(const decFloat *df) { + return !DFISSIGNED(df) && !DFISZERO(df) && !DFISNAN(df); + } +uInt decFloatIsSignaling(const decFloat *df) { + return DFISSNAN(df); + } +uInt decFloatIsSignalling(const decFloat *df) { + return DFISSNAN(df); + } +uInt decFloatIsSigned(const decFloat *df) { + return DFISSIGNED(df); + } +uInt decFloatIsSubnormal(const decFloat *df) { + if (DFISSPECIAL(df)) return 0; + // is finite + if (decFloatIsNormal(df)) return 0; + // it is Use |A| */ +/* A=0 -> -Infinity (Division by zero) */ +/* A=Infinite -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* NaNs are propagated as usual */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatLogB(decFloat *result, const decFloat *df, + decContext *set) { + Int ae; // adjusted exponent + if (DFISNAN(df)) return decNaNs(result, df, NULL, set); + if (DFISINF(df)) { + DFWORD(result, 0)=0; // need +ve + return decInfinity(result, result); // canonical +Infinity + } + if (DFISZERO(df)) { + set->status|=DEC_Division_by_zero; // as per 754 + DFWORD(result, 0)=DECFLOAT_Sign; // make negative + return decInfinity(result, result); // canonical -Infinity + } + ae=GETEXPUN(df) // get unbiased exponent .. + +decFloatDigits(df)-1; // .. and make adjusted exponent + // ae has limited range (3 digits for DOUBLE and 4 for QUAD), so + // it is worth using a special case of decFloatFromInt32 + DFWORD(result, 0)=ZEROWORD; // always + if (ae<0) { + DFWORD(result, 0)|=DECFLOAT_Sign; // -0 so far + ae=-ae; + } + #if DOUBLE + DFWORD(result, 1)=BIN2DPD[ae]; // a single declet + #elif QUAD + DFWORD(result, 1)=0; + DFWORD(result, 2)=0; + DFWORD(result, 3)=(ae/1000)<<10; // is <10, so need no DPD encode + DFWORD(result, 3)|=BIN2DPD[ae%1000]; + #endif + return result; + } // decFloatLogB + +/* ------------------------------------------------------------------ */ +/* decFloatMax -- return maxnum of two operands */ +/* */ +/* result gets the chosen decFloat */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* If just one operand is a quiet NaN it is ignored. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatMax(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int comp; + if (DFISNAN(dfl)) { + // sNaN or both NaNs leads to normal NaN processing + if (DFISNAN(dfr) || DFISSNAN(dfl)) return decNaNs(result, dfl, dfr, set); + return decCanonical(result, dfr); // RHS is numeric + } + if (DFISNAN(dfr)) { + // sNaN leads to normal NaN processing (both NaN handled above) + if (DFISSNAN(dfr)) return decNaNs(result, dfl, dfr, set); + return decCanonical(result, dfl); // LHS is numeric + } + // Both operands are numeric; numeric comparison needed -- use + // total order for a well-defined choice (and +0 > -0) + comp=decNumCompare(dfl, dfr, 1); + if (comp>=0) return decCanonical(result, dfl); + return decCanonical(result, dfr); + } // decFloatMax + +/* ------------------------------------------------------------------ */ +/* decFloatMaxMag -- return maxnummag of two operands */ +/* */ +/* result gets the chosen decFloat */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* Returns according to the magnitude comparisons if both numeric and */ +/* unequal, otherwise returns maxnum */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatMaxMag(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int comp; + decFloat absl, absr; + if (DFISNAN(dfl) || DFISNAN(dfr)) return decFloatMax(result, dfl, dfr, set); + + decFloatCopyAbs(&absl, dfl); + decFloatCopyAbs(&absr, dfr); + comp=decNumCompare(&absl, &absr, 0); + if (comp>0) return decCanonical(result, dfl); + if (comp<0) return decCanonical(result, dfr); + return decFloatMax(result, dfl, dfr, set); + } // decFloatMaxMag + +/* ------------------------------------------------------------------ */ +/* decFloatMin -- return minnum of two operands */ +/* */ +/* result gets the chosen decFloat */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* If just one operand is a quiet NaN it is ignored. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatMin(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int comp; + if (DFISNAN(dfl)) { + // sNaN or both NaNs leads to normal NaN processing + if (DFISNAN(dfr) || DFISSNAN(dfl)) return decNaNs(result, dfl, dfr, set); + return decCanonical(result, dfr); // RHS is numeric + } + if (DFISNAN(dfr)) { + // sNaN leads to normal NaN processing (both NaN handled above) + if (DFISSNAN(dfr)) return decNaNs(result, dfl, dfr, set); + return decCanonical(result, dfl); // LHS is numeric + } + // Both operands are numeric; numeric comparison needed -- use + // total order for a well-defined choice (and +0 > -0) + comp=decNumCompare(dfl, dfr, 1); + if (comp<=0) return decCanonical(result, dfl); + return decCanonical(result, dfr); + } // decFloatMin + +/* ------------------------------------------------------------------ */ +/* decFloatMinMag -- return minnummag of two operands */ +/* */ +/* result gets the chosen decFloat */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* Returns according to the magnitude comparisons if both numeric and */ +/* unequal, otherwise returns minnum */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatMinMag(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int comp; + decFloat absl, absr; + if (DFISNAN(dfl) || DFISNAN(dfr)) return decFloatMin(result, dfl, dfr, set); + + decFloatCopyAbs(&absl, dfl); + decFloatCopyAbs(&absr, dfr); + comp=decNumCompare(&absl, &absr, 0); + if (comp<0) return decCanonical(result, dfl); + if (comp>0) return decCanonical(result, dfr); + return decFloatMin(result, dfl, dfr, set); + } // decFloatMinMag + +/* ------------------------------------------------------------------ */ +/* decFloatMinus -- negate value, heeding NaNs, etc. */ +/* */ +/* result gets the canonicalized 0-df */ +/* df is the decFloat to minus */ +/* set is the context */ +/* returns result */ +/* */ +/* This has the same effect as 0-df where the exponent of the zero is */ +/* the same as that of df (if df is finite). */ +/* The effect is also the same as decFloatCopyNegate except that NaNs */ +/* are handled normally (the sign of a NaN is not affected, and an */ +/* sNaN will signal), the result is canonical, and zero gets sign 0. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatMinus(decFloat *result, const decFloat *df, + decContext *set) { + if (DFISNAN(df)) return decNaNs(result, df, NULL, set); + decCanonical(result, df); // copy and check + if (DFISZERO(df)) DFBYTE(result, 0)&=~0x80; // turn off sign bit + else DFBYTE(result, 0)^=0x80; // flip sign bit + return result; + } // decFloatMinus + +/* ------------------------------------------------------------------ */ +/* decFloatMultiply -- multiply two decFloats */ +/* */ +/* result gets the result of multiplying dfl and dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatMultiply(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + bcdnum num; // for final conversion + uByte bcdacc[DECPMAX9*18+1]; // for coefficent in BCD + + if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr)) { // either is special? + // NaNs are handled as usual + if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + // infinity times zero is bad + if (DFISINF(dfl) && DFISZERO(dfr)) return decInvalid(result, set); + if (DFISINF(dfr) && DFISZERO(dfl)) return decInvalid(result, set); + // both infinite; return canonical infinity with computed sign + DFWORD(result, 0)=DFWORD(dfl, 0)^DFWORD(dfr, 0); // compute sign + return decInfinity(result, result); + } + + /* Here when both operands are finite */ + decFiniteMultiply(&num, bcdacc, dfl, dfr); + return decFinalize(result, &num, set); // round, check, and lay out + } // decFloatMultiply + +/* ------------------------------------------------------------------ */ +/* decFloatNextMinus -- next towards -Infinity */ +/* */ +/* result gets the next lesser decFloat */ +/* dfl is the decFloat to start with */ +/* set is the context */ +/* returns result */ +/* */ +/* This is 754 nextdown; Invalid is the only status possible (from */ +/* an sNaN). */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatNextMinus(decFloat *result, const decFloat *dfl, + decContext *set) { + decFloat delta; // tiny increment + uInt savestat; // saves status + enum rounding saveround; // .. and mode + + // +Infinity is the special case + if (DFISINF(dfl) && !DFISSIGNED(dfl)) { + DFSETNMAX(result); + return result; // [no status to set] + } + // other cases are effected by sutracting a tiny delta -- this + // should be done in a wider format as the delta is unrepresentable + // here (but can be done with normal add if the sign of zero is + // treated carefully, because no Inexactitude is interesting); + // rounding to -Infinity then pushes the result to next below + decFloatZero(&delta); // set up tiny delta + DFWORD(&delta, DECWORDS-1)=1; // coefficient=1 + DFWORD(&delta, 0)=DECFLOAT_Sign; // Sign=1 + biased exponent=0 + // set up for the directional round + saveround=set->round; // save mode + set->round=DEC_ROUND_FLOOR; // .. round towards -Infinity + savestat=set->status; // save status + decFloatAdd(result, dfl, &delta, set); + // Add rules mess up the sign when going from +Ntiny to 0 + if (DFISZERO(result)) DFWORD(result, 0)^=DECFLOAT_Sign; // correct + set->status&=DEC_Invalid_operation; // preserve only sNaN status + set->status|=savestat; // restore pending flags + set->round=saveround; // .. and mode + return result; + } // decFloatNextMinus + +/* ------------------------------------------------------------------ */ +/* decFloatNextPlus -- next towards +Infinity */ +/* */ +/* result gets the next larger decFloat */ +/* dfl is the decFloat to start with */ +/* set is the context */ +/* returns result */ +/* */ +/* This is 754 nextup; Invalid is the only status possible (from */ +/* an sNaN). */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatNextPlus(decFloat *result, const decFloat *dfl, + decContext *set) { + uInt savestat; // saves status + enum rounding saveround; // .. and mode + decFloat delta; // tiny increment + + // -Infinity is the special case + if (DFISINF(dfl) && DFISSIGNED(dfl)) { + DFSETNMAX(result); + DFWORD(result, 0)|=DECFLOAT_Sign; // make negative + return result; // [no status to set] + } + // other cases are effected by sutracting a tiny delta -- this + // should be done in a wider format as the delta is unrepresentable + // here (but can be done with normal add if the sign of zero is + // treated carefully, because no Inexactitude is interesting); + // rounding to +Infinity then pushes the result to next above + decFloatZero(&delta); // set up tiny delta + DFWORD(&delta, DECWORDS-1)=1; // coefficient=1 + DFWORD(&delta, 0)=0; // Sign=0 + biased exponent=0 + // set up for the directional round + saveround=set->round; // save mode + set->round=DEC_ROUND_CEILING; // .. round towards +Infinity + savestat=set->status; // save status + decFloatAdd(result, dfl, &delta, set); + // Add rules mess up the sign when going from -Ntiny to -0 + if (DFISZERO(result)) DFWORD(result, 0)^=DECFLOAT_Sign; // correct + set->status&=DEC_Invalid_operation; // preserve only sNaN status + set->status|=savestat; // restore pending flags + set->round=saveround; // .. and mode + return result; + } // decFloatNextPlus + +/* ------------------------------------------------------------------ */ +/* decFloatNextToward -- next towards a decFloat */ +/* */ +/* result gets the next decFloat */ +/* dfl is the decFloat to start with */ +/* dfr is the decFloat to move toward */ +/* set is the context */ +/* returns result */ +/* */ +/* This is 754-1985 nextafter, as modified during revision (dropped */ +/* from 754-2008); status may be set unless the result is a normal */ +/* number. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatNextToward(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + decFloat delta; // tiny increment or decrement + decFloat pointone; // 1e-1 + uInt savestat; // saves status + enum rounding saveround; // .. and mode + uInt deltatop; // top word for delta + Int comp; // work + + if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + // Both are numeric, so Invalid no longer a possibility + comp=decNumCompare(dfl, dfr, 0); + if (comp==0) return decFloatCopySign(result, dfl, dfr); // equal + // unequal; do NextPlus or NextMinus but with different status rules + + if (comp<0) { // lhsround; // save mode + set->round=DEC_ROUND_CEILING; // .. round towards +Infinity + deltatop=0; // positive delta + } + else { // lhs>rhs, do NextMinus, see above for commentary + if (DFISINF(dfl) && !DFISSIGNED(dfl)) { // +Infinity special case + DFSETNMAX(result); + return result; + } + saveround=set->round; // save mode + set->round=DEC_ROUND_FLOOR; // .. round towards -Infinity + deltatop=DECFLOAT_Sign; // negative delta + } + savestat=set->status; // save status + // Here, Inexact is needed where appropriate (and hence Underflow, + // etc.). Therefore the tiny delta which is otherwise + // unrepresentable (see NextPlus and NextMinus) is constructed + // using the multiplication of FMA. + decFloatZero(&delta); // set up tiny delta + DFWORD(&delta, DECWORDS-1)=1; // coefficient=1 + DFWORD(&delta, 0)=deltatop; // Sign + biased exponent=0 + decFloatFromString(&pointone, "1E-1", set); // set up multiplier + decFloatFMA(result, &delta, &pointone, dfl, set); + // [Delta is truly tiny, so no need to correct sign of zero] + // use new status unless the result is normal + if (decFloatIsNormal(result)) set->status=savestat; // else goes forward + set->round=saveround; // restore mode + return result; + } // decFloatNextToward + +/* ------------------------------------------------------------------ */ +/* decFloatOr -- logical digitwise OR of two decFloats */ +/* */ +/* result gets the result of ORing dfl and dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result, which will be canonical with sign=0 */ +/* */ +/* The operands must be positive, finite with exponent q=0, and */ +/* comprise just zeros and ones; if not, Invalid operation results. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatOr(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + if (!DFISUINT01(dfl) || !DFISUINT01(dfr) + || !DFISCC01(dfl) || !DFISCC01(dfr)) return decInvalid(result, set); + // the operands are positive finite integers (q=0) with just 0s and 1s + #if DOUBLE + DFWORD(result, 0)=ZEROWORD + |((DFWORD(dfl, 0) | DFWORD(dfr, 0))&0x04009124); + DFWORD(result, 1)=(DFWORD(dfl, 1) | DFWORD(dfr, 1))&0x49124491; + #elif QUAD + DFWORD(result, 0)=ZEROWORD + |((DFWORD(dfl, 0) | DFWORD(dfr, 0))&0x04000912); + DFWORD(result, 1)=(DFWORD(dfl, 1) | DFWORD(dfr, 1))&0x44912449; + DFWORD(result, 2)=(DFWORD(dfl, 2) | DFWORD(dfr, 2))&0x12449124; + DFWORD(result, 3)=(DFWORD(dfl, 3) | DFWORD(dfr, 3))&0x49124491; + #endif + return result; + } // decFloatOr + +/* ------------------------------------------------------------------ */ +/* decFloatPlus -- add value to 0, heeding NaNs, etc. */ +/* */ +/* result gets the canonicalized 0+df */ +/* df is the decFloat to plus */ +/* set is the context */ +/* returns result */ +/* */ +/* This has the same effect as 0+df where the exponent of the zero is */ +/* the same as that of df (if df is finite). */ +/* The effect is also the same as decFloatCopy except that NaNs */ +/* are handled normally (the sign of a NaN is not affected, and an */ +/* sNaN will signal), the result is canonical, and zero gets sign 0. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatPlus(decFloat *result, const decFloat *df, + decContext *set) { + if (DFISNAN(df)) return decNaNs(result, df, NULL, set); + decCanonical(result, df); // copy and check + if (DFISZERO(df)) DFBYTE(result, 0)&=~0x80; // turn off sign bit + return result; + } // decFloatPlus + +/* ------------------------------------------------------------------ */ +/* decFloatQuantize -- quantize a decFloat */ +/* */ +/* result gets the result of quantizing dfl to match dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs), which sets the exponent */ +/* set is the context */ +/* returns result */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* of result is guaranteed to be the same as that of dfr. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatQuantize(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int explb, exprb; // left and right biased exponents + uByte *ulsd; // local LSD pointer + uByte *ub, *uc; // work + Int drop; // .. + uInt dpd; // .. + uInt encode; // encoding accumulator + uInt sourhil, sourhir; // top words from source decFloats + uInt uiwork; // for macros + #if QUAD + uShort uswork; // .. + #endif + // the following buffer holds the coefficient for manipulation + uByte buf[4+DECPMAX*3+2*QUAD]; // + space for zeros to left or right + #if DECTRACE + bcdnum num; // for trace displays + #endif + + /* Start decoding the arguments */ + sourhil=DFWORD(dfl, 0); // LHS top word + explb=DECCOMBEXP[sourhil>>26]; // get exponent high bits (in place) + sourhir=DFWORD(dfr, 0); // RHS top word + exprb=DECCOMBEXP[sourhir>>26]; + + if (EXPISSPECIAL(explb | exprb)) { // either is special? + // NaNs are handled as usual + if (DFISNAN(dfl) || DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + // one infinity but not both is bad + if (DFISINF(dfl)!=DFISINF(dfr)) return decInvalid(result, set); + // both infinite; return canonical infinity with sign of LHS + return decInfinity(result, dfl); + } + + /* Here when both arguments are finite */ + // complete extraction of the exponents [no need to unbias] + explb+=GETECON(dfl); // + continuation + exprb+=GETECON(dfr); // .. + + // calculate the number of digits to drop from the coefficient + drop=exprb-explb; // 0 if nothing to do + if (drop==0) return decCanonical(result, dfl); // return canonical + + // the coefficient is needed; lay it out into buf, offset so zeros + // can be added before or after as needed -- an extra heading is + // added so can safely pad Quad DECPMAX-1 zeros to the left by + // fours + #define BUFOFF (buf+4+DECPMAX) + GETCOEFF(dfl, BUFOFF); // decode from decFloat + // [now the msd is at BUFOFF and the lsd is at BUFOFF+DECPMAX-1] + + #if DECTRACE + num.msd=BUFOFF; + num.lsd=BUFOFF+DECPMAX-1; + num.exponent=explb-DECBIAS; + num.sign=sourhil & DECFLOAT_Sign; + decShowNum(&num, "dfl"); + #endif + + if (drop>0) { // [most common case] + // (this code is very similar to that in decFloatFinalize, but + // has many differences so is duplicated here -- so any changes + // may need to be made there, too) + uByte *roundat; // -> re-round digit + uByte reround; // reround value + // printf("Rounding; drop=%ld\n", (LI)drop); + + // there is at least one zero needed to the left, in all but one + // exceptional (all-nines) case, so place four zeros now; this is + // needed almost always and makes rounding all-nines by fours safe + UBFROMUI(BUFOFF-4, 0); + + // Three cases here: + // 1. new LSD is in coefficient (almost always) + // 2. new LSD is digit to left of coefficient (so MSD is + // round-for-reround digit) + // 3. new LSD is to left of case 2 (whole coefficient is sticky) + // Note that leading zeros can safely be treated as useful digits + + // [duplicate check-stickies code to save a test] + // [by-digit check for stickies as runs of zeros are rare] + if (dropstatus|=DEC_Inexact; + + // next decide whether to increment the coefficient + if (set->round==DEC_ROUND_HALF_EVEN) { // fastpath slowest case + if (reround>5) bump=1; // >0.5 goes up + else if (reround==5) // exactly 0.5000 .. + bump=*ulsd & 0x01; // .. up iff [new] lsd is odd + } // r-h-e + else switch (set->round) { + case DEC_ROUND_DOWN: { + // no change + break;} // r-d + case DEC_ROUND_HALF_DOWN: { + if (reround>5) bump=1; + break;} // r-h-d + case DEC_ROUND_HALF_UP: { + if (reround>=5) bump=1; + break;} // r-h-u + case DEC_ROUND_UP: { + if (reround>0) bump=1; + break;} // r-u + case DEC_ROUND_CEILING: { + // same as _UP for positive numbers, and as _DOWN for negatives + if (!(sourhil&DECFLOAT_Sign) && reround>0) bump=1; + break;} // r-c + case DEC_ROUND_FLOOR: { + // same as _UP for negative numbers, and as _DOWN for positive + // [negative reround cannot occur on 0] + if (sourhil&DECFLOAT_Sign && reround>0) bump=1; + break;} // r-f + case DEC_ROUND_05UP: { + if (reround>0) { // anything out there is 'sticky' + // bump iff lsd=0 or 5; this cannot carry so it could be + // effected immediately with no bump -- but the code + // is clearer if this is done the same way as the others + if (*ulsd==0 || *ulsd==5) bump=1; + } + break;} // r-r + default: { // e.g., DEC_ROUND_MAX + set->status|=DEC_Invalid_context; + #if DECCHECK + printf("Unknown rounding mode: %ld\n", (LI)set->round); + #endif + break;} + } // switch (not r-h-e) + // printf("ReRound: %ld bump: %ld\n", (LI)reround, (LI)bump); + + if (bump!=0) { // need increment + // increment the coefficient; this could give 1000... (after + // the all nines case) + ub=ulsd; + for (; UBTOUI(ub-3)==0x09090909; ub-=4) UBFROMUI(ub-3, 0); + // now at most 3 digits left to non-9 (usually just the one) + for (; *ub==9; ub--) *ub=0; + *ub+=1; + // [the all-nines case will have carried one digit to the + // left of the original MSD -- just where it is needed] + } // bump needed + } // inexact rounding + + // now clear zeros to the left so exactly DECPMAX digits will be + // available in the coefficent -- the first word to the left was + // cleared earlier for safe carry; now add any more needed + if (drop>4) { + UBFROMUI(BUFOFF-8, 0); // must be at least 5 + for (uc=BUFOFF-12; uc>ulsd-DECPMAX-3; uc-=4) UBFROMUI(uc, 0); + } + } // need round (drop>0) + + else { // drop<0; padding with -drop digits is needed + // This is the case where an error can occur if the padded + // coefficient will not fit; checking for this can be done in the + // same loop as padding for zeros if the no-hope and zero cases + // are checked first + if (-drop>DECPMAX-1) { // cannot fit unless 0 + if (!ISCOEFFZERO(BUFOFF)) return decInvalid(result, set); + // a zero can have any exponent; just drop through and use it + ulsd=BUFOFF+DECPMAX-1; + } + else { // padding will fit (but may still be too long) + // final-word mask depends on endianess + #if DECLITEND + static const uInt dmask[]={0, 0x000000ff, 0x0000ffff, 0x00ffffff}; + #else + static const uInt dmask[]={0, 0xff000000, 0xffff0000, 0xffffff00}; + #endif + // note that here zeros to the right are added by fours, so in + // the Quad case this could write 36 zeros if the coefficient has + // fewer than three significant digits (hence the +2*QUAD for buf) + for (uc=BUFOFF+DECPMAX;; uc+=4) { + UBFROMUI(uc, 0); + if (UBTOUI(uc-DECPMAX)!=0) { // could be bad + // if all four digits should be zero, definitely bad + if (uc<=BUFOFF+DECPMAX+(-drop)-4) + return decInvalid(result, set); + // must be a 1- to 3-digit sequence; check more carefully + if ((UBTOUI(uc-DECPMAX)&dmask[(-drop)%4])!=0) + return decInvalid(result, set); + break; // no need for loop end test + } + if (uc>=BUFOFF+DECPMAX+(-drop)-4) break; // done + } + ulsd=BUFOFF+DECPMAX+(-drop)-1; + } // pad and check leading zeros + } // drop<0 + + #if DECTRACE + num.msd=ulsd-DECPMAX+1; + num.lsd=ulsd; + num.exponent=explb-DECBIAS; + num.sign=sourhil & DECFLOAT_Sign; + decShowNum(&num, "res"); + #endif + + /*------------------------------------------------------------------*/ + /* At this point the result is DECPMAX digits, ending at ulsd, so */ + /* fits the encoding exactly; there is no possibility of error */ + /*------------------------------------------------------------------*/ + encode=((exprb>>DECECONL)<<4) + *(ulsd-DECPMAX+1); // make index + encode=DECCOMBFROM[encode]; // indexed by (0-2)*16+msd + // the exponent continuation can be extracted from the original RHS + encode|=sourhir & ECONMASK; + encode|=sourhil&DECFLOAT_Sign; // add the sign from LHS + + // finally encode the coefficient + // private macro to encode a declet; this version can be used + // because all coefficient digits exist + #define getDPD3q(dpd, n) ub=ulsd-(3*(n))-2; \ + dpd=BCD2DPD[(*ub*256)+(*(ub+1)*16)+*(ub+2)]; + + #if DOUBLE + getDPD3q(dpd, 4); encode|=dpd<<8; + getDPD3q(dpd, 3); encode|=dpd>>2; + DFWORD(result, 0)=encode; + encode=dpd<<30; + getDPD3q(dpd, 2); encode|=dpd<<20; + getDPD3q(dpd, 1); encode|=dpd<<10; + getDPD3q(dpd, 0); encode|=dpd; + DFWORD(result, 1)=encode; + + #elif QUAD + getDPD3q(dpd,10); encode|=dpd<<4; + getDPD3q(dpd, 9); encode|=dpd>>6; + DFWORD(result, 0)=encode; + encode=dpd<<26; + getDPD3q(dpd, 8); encode|=dpd<<16; + getDPD3q(dpd, 7); encode|=dpd<<6; + getDPD3q(dpd, 6); encode|=dpd>>4; + DFWORD(result, 1)=encode; + encode=dpd<<28; + getDPD3q(dpd, 5); encode|=dpd<<18; + getDPD3q(dpd, 4); encode|=dpd<<8; + getDPD3q(dpd, 3); encode|=dpd>>2; + DFWORD(result, 2)=encode; + encode=dpd<<30; + getDPD3q(dpd, 2); encode|=dpd<<20; + getDPD3q(dpd, 1); encode|=dpd<<10; + getDPD3q(dpd, 0); encode|=dpd; + DFWORD(result, 3)=encode; + #endif + return result; + } // decFloatQuantize + +/* ------------------------------------------------------------------ */ +/* decFloatReduce -- reduce finite coefficient to minimum length */ +/* */ +/* result gets the reduced decFloat */ +/* df is the source decFloat */ +/* set is the context */ +/* returns result, which will be canonical */ +/* */ +/* This removes all possible trailing zeros from the coefficient; */ +/* some may remain when the number is very close to Nmax. */ +/* Special values are unchanged and no status is set unless df=sNaN. */ +/* Reduced zero has an exponent q=0. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatReduce(decFloat *result, const decFloat *df, + decContext *set) { + bcdnum num; // work + uByte buf[DECPMAX], *ub; // coefficient and pointer + if (df!=result) *result=*df; // copy, if needed + if (DFISNAN(df)) return decNaNs(result, df, NULL, set); // sNaN + // zeros and infinites propagate too + if (DFISINF(df)) return decInfinity(result, df); // canonical + if (DFISZERO(df)) { + uInt sign=DFWORD(df, 0)&DECFLOAT_Sign; + decFloatZero(result); + DFWORD(result, 0)|=sign; + return result; // exponent dropped, sign OK + } + // non-zero finite + GETCOEFF(df, buf); + ub=buf+DECPMAX-1; // -> lsd + if (*ub) return result; // no trailing zeros + for (ub--; *ub==0;) ub--; // terminates because non-zero + // *ub is the first non-zero from the right + num.sign=DFWORD(df, 0)&DECFLOAT_Sign; // set up number... + num.exponent=GETEXPUN(df)+(Int)(buf+DECPMAX-1-ub); // adjusted exponent + num.msd=buf; + num.lsd=ub; + return decFinalize(result, &num, set); + } // decFloatReduce + +/* ------------------------------------------------------------------ */ +/* decFloatRemainder -- integer divide and return remainder */ +/* */ +/* result gets the remainder of dividing dfl by dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatRemainder(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + return decDivide(result, dfl, dfr, set, REMAINDER); + } // decFloatRemainder + +/* ------------------------------------------------------------------ */ +/* decFloatRemainderNear -- integer divide to nearest and remainder */ +/* */ +/* result gets the remainder of dividing dfl by dfr: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* This is the IEEE remainder, where the nearest integer is used. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatRemainderNear(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + return decDivide(result, dfl, dfr, set, REMNEAR); + } // decFloatRemainderNear + +/* ------------------------------------------------------------------ */ +/* decFloatRotate -- rotate the coefficient of a decFloat left/right */ +/* */ +/* result gets the result of rotating dfl */ +/* dfl is the source decFloat to rotate */ +/* dfr is the count of digits to rotate, an integer (with q=0) */ +/* set is the context */ +/* returns result */ +/* */ +/* The digits of the coefficient of dfl are rotated to the left (if */ +/* dfr is positive) or to the right (if dfr is negative) without */ +/* adjusting the exponent or the sign of dfl. */ +/* */ +/* dfr must be in the range -DECPMAX through +DECPMAX. */ +/* NaNs are propagated as usual. An infinite dfl is unaffected (but */ +/* dfr must be valid). No status is set unless dfr is invalid or an */ +/* operand is an sNaN. The result is canonical. */ +/* ------------------------------------------------------------------ */ +#define PHALF (ROUNDUP(DECPMAX/2, 4)) // half length, rounded up +decFloat * decFloatRotate(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int rotate; // dfr as an Int + uByte buf[DECPMAX+PHALF]; // coefficient + half + uInt digits, savestat; // work + bcdnum num; // .. + uByte *ub; // .. + + if (DFISNAN(dfl)||DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + if (!DFISINT(dfr)) return decInvalid(result, set); + digits=decFloatDigits(dfr); // calculate digits + if (digits>2) return decInvalid(result, set); // definitely out of range + rotate=DPD2BIN[DFWORD(dfr, DECWORDS-1)&0x3ff]; // is in bottom declet + if (rotate>DECPMAX) return decInvalid(result, set); // too big + // [from here on no error or status change is possible] + if (DFISINF(dfl)) return decInfinity(result, dfl); // canonical + // handle no-rotate cases + if (rotate==0 || rotate==DECPMAX) return decCanonical(result, dfl); + // a real rotate is needed: 0 < rotate < DECPMAX + // reduce the rotation to no more than half to reduce copying later + // (for QUAD in fact half + 2 digits) + if (DFISSIGNED(dfr)) rotate=-rotate; + if (abs(rotate)>PHALF) { + if (rotate<0) rotate=DECPMAX+rotate; + else rotate=rotate-DECPMAX; + } + // now lay out the coefficient, leaving room to the right or the + // left depending on the direction of rotation + ub=buf; + if (rotate<0) ub+=PHALF; // rotate right, so space to left + GETCOEFF(dfl, ub); + // copy half the digits to left or right, and set num.msd + if (rotate<0) { + memcpy(buf, buf+DECPMAX, PHALF); + num.msd=buf+PHALF+rotate; + } + else { + memcpy(buf+DECPMAX, buf, PHALF); + num.msd=buf+rotate; + } + // fill in rest of num + num.lsd=num.msd+DECPMAX-1; + num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign; + num.exponent=GETEXPUN(dfl); + savestat=set->status; // record + decFinalize(result, &num, set); + set->status=savestat; // restore + return result; + } // decFloatRotate + +/* ------------------------------------------------------------------ */ +/* decFloatSameQuantum -- test decFloats for same quantum */ +/* */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* returns 1 if the operands have the same quantum, 0 otherwise */ +/* */ +/* No error is possible and no status results. */ +/* ------------------------------------------------------------------ */ +uInt decFloatSameQuantum(const decFloat *dfl, const decFloat *dfr) { + if (DFISSPECIAL(dfl) || DFISSPECIAL(dfr)) { + if (DFISNAN(dfl) && DFISNAN(dfr)) return 1; + if (DFISINF(dfl) && DFISINF(dfr)) return 1; + return 0; // any other special mixture gives false + } + if (GETEXP(dfl)==GETEXP(dfr)) return 1; // biased exponents match + return 0; + } // decFloatSameQuantum + +/* ------------------------------------------------------------------ */ +/* decFloatScaleB -- multiply by a power of 10, as per 754 */ +/* */ +/* result gets the result of the operation */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs), am integer (with q=0) */ +/* set is the context */ +/* returns result */ +/* */ +/* This computes result=dfl x 10**dfr where dfr is an integer in the */ +/* range +/-2*(emax+pmax), typically resulting from LogB. */ +/* Underflow and Overflow (with Inexact) may occur. NaNs propagate */ +/* as usual. */ +/* ------------------------------------------------------------------ */ +#define SCALEBMAX 2*(DECEMAX+DECPMAX) // D=800, Q=12356 +decFloat * decFloatScaleB(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + uInt digits; // work + Int expr; // dfr as an Int + + if (DFISNAN(dfl)||DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + if (!DFISINT(dfr)) return decInvalid(result, set); + digits=decFloatDigits(dfr); // calculate digits + + #if DOUBLE + if (digits>3) return decInvalid(result, set); // definitely out of range + expr=DPD2BIN[DFWORD(dfr, 1)&0x3ff]; // must be in bottom declet + #elif QUAD + if (digits>5) return decInvalid(result, set); // definitely out of range + expr=DPD2BIN[DFWORD(dfr, 3)&0x3ff] // in bottom 2 declets .. + +DPD2BIN[(DFWORD(dfr, 3)>>10)&0x3ff]*1000; // .. + #endif + if (expr>SCALEBMAX) return decInvalid(result, set); // oops + // [from now on no error possible] + if (DFISINF(dfl)) return decInfinity(result, dfl); // canonical + if (DFISSIGNED(dfr)) expr=-expr; + // dfl is finite and expr is valid + *result=*dfl; // copy to target + return decFloatSetExponent(result, set, GETEXPUN(result)+expr); + } // decFloatScaleB + +/* ------------------------------------------------------------------ */ +/* decFloatShift -- shift the coefficient of a decFloat left or right */ +/* */ +/* result gets the result of shifting dfl */ +/* dfl is the source decFloat to shift */ +/* dfr is the count of digits to shift, an integer (with q=0) */ +/* set is the context */ +/* returns result */ +/* */ +/* The digits of the coefficient of dfl are shifted to the left (if */ +/* dfr is positive) or to the right (if dfr is negative) without */ +/* adjusting the exponent or the sign of dfl. */ +/* */ +/* dfr must be in the range -DECPMAX through +DECPMAX. */ +/* NaNs are propagated as usual. An infinite dfl is unaffected (but */ +/* dfr must be valid). No status is set unless dfr is invalid or an */ +/* operand is an sNaN. The result is canonical. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatShift(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + Int shift; // dfr as an Int + uByte buf[DECPMAX*2]; // coefficient + padding + uInt digits, savestat; // work + bcdnum num; // .. + uInt uiwork; // for macros + + if (DFISNAN(dfl)||DFISNAN(dfr)) return decNaNs(result, dfl, dfr, set); + if (!DFISINT(dfr)) return decInvalid(result, set); + digits=decFloatDigits(dfr); // calculate digits + if (digits>2) return decInvalid(result, set); // definitely out of range + shift=DPD2BIN[DFWORD(dfr, DECWORDS-1)&0x3ff]; // is in bottom declet + if (shift>DECPMAX) return decInvalid(result, set); // too big + // [from here on no error or status change is possible] + + if (DFISINF(dfl)) return decInfinity(result, dfl); // canonical + // handle no-shift and all-shift (clear to zero) cases + if (shift==0) return decCanonical(result, dfl); + if (shift==DECPMAX) { // zero with sign + uByte sign=(uByte)(DFBYTE(dfl, 0)&0x80); // save sign bit + decFloatZero(result); // make +0 + DFBYTE(result, 0)=(uByte)(DFBYTE(result, 0)|sign); // and set sign + // [cannot safely use CopySign] + return result; + } + // a real shift is needed: 0 < shift < DECPMAX + num.sign=DFWORD(dfl, 0)&DECFLOAT_Sign; + num.exponent=GETEXPUN(dfl); + num.msd=buf; + GETCOEFF(dfl, buf); + if (DFISSIGNED(dfr)) { // shift right + // edge cases are taken care of, so this is easy + num.lsd=buf+DECPMAX-shift-1; + } + else { // shift left -- zero padding needed to right + UBFROMUI(buf+DECPMAX, 0); // 8 will handle most cases + UBFROMUI(buf+DECPMAX+4, 0); // .. + if (shift>8) memset(buf+DECPMAX+8, 0, 8+QUAD*18); // all other cases + num.msd+=shift; + num.lsd=num.msd+DECPMAX-1; + } + savestat=set->status; // record + decFinalize(result, &num, set); + set->status=savestat; // restore + return result; + } // decFloatShift + +/* ------------------------------------------------------------------ */ +/* decFloatSubtract -- subtract a decFloat from another */ +/* */ +/* result gets the result of subtracting dfr from dfl: */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result */ +/* */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatSubtract(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + decFloat temp; + // NaNs must propagate without sign change + if (DFISNAN(dfr)) return decFloatAdd(result, dfl, dfr, set); + temp=*dfr; // make a copy + DFBYTE(&temp, 0)^=0x80; // flip sign + return decFloatAdd(result, dfl, &temp, set); // and add to the lhs + } // decFloatSubtract + +/* ------------------------------------------------------------------ */ +/* decFloatToInt -- round to 32-bit binary integer (4 flavours) */ +/* */ +/* df is the decFloat to round */ +/* set is the context */ +/* round is the rounding mode to use */ +/* returns a uInt or an Int, rounded according to the name */ +/* */ +/* Invalid will always be signaled if df is a NaN, is Infinite, or is */ +/* outside the range of the target; Inexact will not be signaled for */ +/* simple rounding unless 'Exact' appears in the name. */ +/* ------------------------------------------------------------------ */ +uInt decFloatToUInt32(const decFloat *df, decContext *set, + enum rounding round) { + return decToInt32(df, set, round, 0, 1);} + +uInt decFloatToUInt32Exact(const decFloat *df, decContext *set, + enum rounding round) { + return decToInt32(df, set, round, 1, 1);} + +Int decFloatToInt32(const decFloat *df, decContext *set, + enum rounding round) { + return (Int)decToInt32(df, set, round, 0, 0);} + +Int decFloatToInt32Exact(const decFloat *df, decContext *set, + enum rounding round) { + return (Int)decToInt32(df, set, round, 1, 0);} + +/* ------------------------------------------------------------------ */ +/* decFloatToIntegral -- round to integral value (two flavours) */ +/* */ +/* result gets the result */ +/* df is the decFloat to round */ +/* set is the context */ +/* round is the rounding mode to use */ +/* returns result */ +/* */ +/* No exceptions, even Inexact, are raised except for sNaN input, or */ +/* if 'Exact' appears in the name. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatToIntegralValue(decFloat *result, const decFloat *df, + decContext *set, enum rounding round) { + return decToIntegral(result, df, set, round, 0);} + +decFloat * decFloatToIntegralExact(decFloat *result, const decFloat *df, + decContext *set) { + return decToIntegral(result, df, set, set->round, 1);} + +/* ------------------------------------------------------------------ */ +/* decFloatXor -- logical digitwise XOR of two decFloats */ +/* */ +/* result gets the result of XORing dfl and dfr */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) */ +/* set is the context */ +/* returns result, which will be canonical with sign=0 */ +/* */ +/* The operands must be positive, finite with exponent q=0, and */ +/* comprise just zeros and ones; if not, Invalid operation results. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatXor(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + if (!DFISUINT01(dfl) || !DFISUINT01(dfr) + || !DFISCC01(dfl) || !DFISCC01(dfr)) return decInvalid(result, set); + // the operands are positive finite integers (q=0) with just 0s and 1s + #if DOUBLE + DFWORD(result, 0)=ZEROWORD + |((DFWORD(dfl, 0) ^ DFWORD(dfr, 0))&0x04009124); + DFWORD(result, 1)=(DFWORD(dfl, 1) ^ DFWORD(dfr, 1))&0x49124491; + #elif QUAD + DFWORD(result, 0)=ZEROWORD + |((DFWORD(dfl, 0) ^ DFWORD(dfr, 0))&0x04000912); + DFWORD(result, 1)=(DFWORD(dfl, 1) ^ DFWORD(dfr, 1))&0x44912449; + DFWORD(result, 2)=(DFWORD(dfl, 2) ^ DFWORD(dfr, 2))&0x12449124; + DFWORD(result, 3)=(DFWORD(dfl, 3) ^ DFWORD(dfr, 3))&0x49124491; + #endif + return result; + } // decFloatXor + +/* ------------------------------------------------------------------ */ +/* decInvalid -- set Invalid_operation result */ +/* */ +/* result gets a canonical NaN */ +/* set is the context */ +/* returns result */ +/* */ +/* status has Invalid_operation added */ +/* ------------------------------------------------------------------ */ +static decFloat *decInvalid(decFloat *result, decContext *set) { + decFloatZero(result); + DFWORD(result, 0)=DECFLOAT_qNaN; + set->status|=DEC_Invalid_operation; + return result; + } // decInvalid + +/* ------------------------------------------------------------------ */ +/* decInfinity -- set canonical Infinity with sign from a decFloat */ +/* */ +/* result gets a canonical Infinity */ +/* df is source decFloat (only the sign is used) */ +/* returns result */ +/* */ +/* df may be the same as result */ +/* ------------------------------------------------------------------ */ +static decFloat *decInfinity(decFloat *result, const decFloat *df) { + uInt sign=DFWORD(df, 0); // save source signword + decFloatZero(result); // clear everything + DFWORD(result, 0)=DECFLOAT_Inf | (sign & DECFLOAT_Sign); + return result; + } // decInfinity + +/* ------------------------------------------------------------------ */ +/* decNaNs -- handle NaN argument(s) */ +/* */ +/* result gets the result of handling dfl and dfr, one or both of */ +/* which is a NaN */ +/* dfl is the first decFloat (lhs) */ +/* dfr is the second decFloat (rhs) -- may be NULL for a single- */ +/* operand operation */ +/* set is the context */ +/* returns result */ +/* */ +/* Called when one or both operands is a NaN, and propagates the */ +/* appropriate result to res. When an sNaN is found, it is changed */ +/* to a qNaN and Invalid operation is set. */ +/* ------------------------------------------------------------------ */ +static decFloat *decNaNs(decFloat *result, + const decFloat *dfl, const decFloat *dfr, + decContext *set) { + // handle sNaNs first + if (dfr!=NULL && DFISSNAN(dfr) && !DFISSNAN(dfl)) dfl=dfr; // use RHS + if (DFISSNAN(dfl)) { + decCanonical(result, dfl); // propagate canonical sNaN + DFWORD(result, 0)&=~(DECFLOAT_qNaN ^ DECFLOAT_sNaN); // quiet + set->status|=DEC_Invalid_operation; + return result; + } + // one or both is a quiet NaN + if (!DFISNAN(dfl)) dfl=dfr; // RHS must be NaN, use it + return decCanonical(result, dfl); // propagate canonical qNaN + } // decNaNs + +/* ------------------------------------------------------------------ */ +/* decNumCompare -- numeric comparison of two decFloats */ +/* */ +/* dfl is the left-hand decFloat, which is not a NaN */ +/* dfr is the right-hand decFloat, which is not a NaN */ +/* tot is 1 for total order compare, 0 for simple numeric */ +/* returns -1, 0, or +1 for dfldfr */ +/* */ +/* No error is possible; status and mode are unchanged. */ +/* ------------------------------------------------------------------ */ +static Int decNumCompare(const decFloat *dfl, const decFloat *dfr, Flag tot) { + Int sigl, sigr; // LHS and RHS non-0 signums + Int shift; // shift needed to align operands + uByte *ub, *uc; // work + uInt uiwork; // for macros + // buffers +2 if Quad (36 digits), need double plus 4 for safe padding + uByte bufl[DECPMAX*2+QUAD*2+4]; // for LHS coefficient + padding + uByte bufr[DECPMAX*2+QUAD*2+4]; // for RHS coefficient + padding + + sigl=1; + if (DFISSIGNED(dfl)) { + if (!DFISSIGNED(dfr)) { // -LHS +RHS + if (DFISZERO(dfl) && DFISZERO(dfr) && !tot) return 0; + return -1; // RHS wins + } + sigl=-1; + } + if (DFISSIGNED(dfr)) { + if (!DFISSIGNED(dfl)) { // +LHS -RHS + if (DFISZERO(dfl) && DFISZERO(dfr) && !tot) return 0; + return +1; // LHS wins + } + } + + // signs are the same; operand(s) could be zero + sigr=-sigl; // sign to return if abs(RHS) wins + + if (DFISINF(dfl)) { + if (DFISINF(dfr)) return 0; // both infinite & same sign + return sigl; // inf > n + } + if (DFISINF(dfr)) return sigr; // n < inf [dfl is finite] + + // here, both are same sign and finite; calculate their offset + shift=GETEXP(dfl)-GETEXP(dfr); // [0 means aligned] + // [bias can be ignored -- the absolute exponent is not relevant] + + if (DFISZERO(dfl)) { + if (!DFISZERO(dfr)) return sigr; // LHS=0, RHS!=0 + // both are zero, return 0 if both same exponent or numeric compare + if (shift==0 || !tot) return 0; + if (shift>0) return sigl; + return sigr; // [shift<0] + } + else { // LHS!=0 + if (DFISZERO(dfr)) return sigl; // LHS!=0, RHS=0 + } + // both are known to be non-zero at this point + + // if the exponents are so different that the coefficients do not + // overlap (by even one digit) then a full comparison is not needed + if (abs(shift)>=DECPMAX) { // no overlap + // coefficients are known to be non-zero + if (shift>0) return sigl; + return sigr; // [shift<0] + } + + // decode the coefficients + // (shift both right two if Quad to make a multiple of four) + #if QUAD + UBFROMUI(bufl, 0); + UBFROMUI(bufr, 0); + #endif + GETCOEFF(dfl, bufl+QUAD*2); // decode from decFloat + GETCOEFF(dfr, bufr+QUAD*2); // .. + if (shift==0) { // aligned; common and easy + // all multiples of four, here + for (ub=bufl, uc=bufr; ub*uc) return sigl; // difference found + if (*ub<*uc) return sigr; // .. + } + } + } // aligned + else if (shift>0) { // lhs to left + ub=bufl; // RHS pointer + // pad bufl so right-aligned; most shifts will fit in 8 + UBFROMUI(bufl+DECPMAX+QUAD*2, 0); // add eight zeros + UBFROMUI(bufl+DECPMAX+QUAD*2+4, 0); // .. + if (shift>8) { + // more than eight; fill the rest, and also worth doing the + // lead-in by fours + uByte *up; // work + uByte *upend=bufl+DECPMAX+QUAD*2+shift; + for (up=bufl+DECPMAX+QUAD*2+8; upbufl+shift-4) break; + } + } + // check remaining leading digits + for (; ub*uc) return sigl; // difference found + if (*ub<*uc) return sigr; // .. + } + } // mismatch + if (uc==bufr+QUAD*2+DECPMAX-4) break; // all checked + } + } // shift>0 + + else { // shift<0) .. RHS is to left of LHS; mirror shift>0 + uc=bufr; // RHS pointer + // pad bufr so right-aligned; most shifts will fit in 8 + UBFROMUI(bufr+DECPMAX+QUAD*2, 0); // add eight zeros + UBFROMUI(bufr+DECPMAX+QUAD*2+4, 0); // .. + if (shift<-8) { + // more than eight; fill the rest, and also worth doing the + // lead-in by fours + uByte *up; // work + uByte *upend=bufr+DECPMAX+QUAD*2-shift; + for (up=bufr+DECPMAX+QUAD*2+8; upbufr-shift-4) break; + } + } + // check remaining leading digits + for (; uc*uc) return sigl; // difference found + if (*ub<*uc) return sigr; // .. + } + } // mismatch + if (ub==bufl+QUAD*2+DECPMAX-4) break; // all checked + } + } // shift<0 + + // Here when compare equal + if (!tot) return 0; // numerically equal + // total ordering .. exponent matters + if (shift>0) return sigl; // total order by exponent + if (shift<0) return sigr; // .. + return 0; + } // decNumCompare + +/* ------------------------------------------------------------------ */ +/* decToInt32 -- local routine to effect ToInteger conversions */ +/* */ +/* df is the decFloat to convert */ +/* set is the context */ +/* rmode is the rounding mode to use */ +/* exact is 1 if Inexact should be signalled */ +/* unsign is 1 if the result a uInt, 0 if an Int (cast to uInt) */ +/* returns 32-bit result as a uInt */ +/* */ +/* Invalid is set is df is a NaN, is infinite, or is out-of-range; in */ +/* these cases 0 is returned. */ +/* ------------------------------------------------------------------ */ +static uInt decToInt32(const decFloat *df, decContext *set, + enum rounding rmode, Flag exact, Flag unsign) { + Int exp; // exponent + uInt sourhi, sourpen, sourlo; // top word from source decFloat .. + uInt hi, lo; // .. penultimate, least, etc. + decFloat zero, result; // work + Int i; // .. + + /* Start decoding the argument */ + sourhi=DFWORD(df, 0); // top word + exp=DECCOMBEXP[sourhi>>26]; // get exponent high bits (in place) + if (EXPISSPECIAL(exp)) { // is special? + set->status|=DEC_Invalid_operation; // signal + return 0; + } + + /* Here when the argument is finite */ + if (GETEXPUN(df)==0) result=*df; // already a true integer + else { // need to round to integer + enum rounding saveround; // saver + uInt savestatus; // .. + saveround=set->round; // save rounding mode .. + savestatus=set->status; // .. and status + set->round=rmode; // set mode + decFloatZero(&zero); // make 0E+0 + set->status=0; // clear + decFloatQuantize(&result, df, &zero, set); // [this may fail] + set->round=saveround; // restore rounding mode .. + if (exact) set->status|=savestatus; // include Inexact + else set->status=savestatus; // .. or just original status + } + + // only the last four declets of the coefficient can contain + // non-zero; check for others (and also NaN or Infinity from the + // Quantize) first (see DFISZERO for explanation): + // decFloatShow(&result, "sofar"); + #if DOUBLE + if ((DFWORD(&result, 0)&0x1c03ff00)!=0 + || (DFWORD(&result, 0)&0x60000000)==0x60000000) { + #elif QUAD + if ((DFWORD(&result, 2)&0xffffff00)!=0 + || DFWORD(&result, 1)!=0 + || (DFWORD(&result, 0)&0x1c003fff)!=0 + || (DFWORD(&result, 0)&0x60000000)==0x60000000) { + #endif + set->status|=DEC_Invalid_operation; // Invalid or out of range + return 0; + } + // get last twelve digits of the coefficent into hi & ho, base + // 10**9 (see GETCOEFFBILL): + sourlo=DFWORD(&result, DECWORDS-1); + lo=DPD2BIN0[sourlo&0x3ff] + +DPD2BINK[(sourlo>>10)&0x3ff] + +DPD2BINM[(sourlo>>20)&0x3ff]; + sourpen=DFWORD(&result, DECWORDS-2); + hi=DPD2BIN0[((sourpen<<2) | (sourlo>>30))&0x3ff]; + + // according to request, check range carefully + if (unsign) { + if (hi>4 || (hi==4 && lo>294967295) || (hi+lo!=0 && DFISSIGNED(&result))) { + set->status|=DEC_Invalid_operation; // out of range + return 0; + } + return hi*BILLION+lo; + } + // signed + if (hi>2 || (hi==2 && lo>147483647)) { + // handle the usual edge case + if (lo==147483648 && hi==2 && DFISSIGNED(&result)) return 0x80000000; + set->status|=DEC_Invalid_operation; // truly out of range + return 0; + } + i=hi*BILLION+lo; + if (DFISSIGNED(&result)) i=-i; + return (uInt)i; + } // decToInt32 + +/* ------------------------------------------------------------------ */ +/* decToIntegral -- local routine to effect ToIntegral value */ +/* */ +/* result gets the result */ +/* df is the decFloat to round */ +/* set is the context */ +/* rmode is the rounding mode to use */ +/* exact is 1 if Inexact should be signalled */ +/* returns result */ +/* ------------------------------------------------------------------ */ +static decFloat * decToIntegral(decFloat *result, const decFloat *df, + decContext *set, enum rounding rmode, + Flag exact) { + Int exp; // exponent + uInt sourhi; // top word from source decFloat + enum rounding saveround; // saver + uInt savestatus; // .. + decFloat zero; // work + + /* Start decoding the argument */ + sourhi=DFWORD(df, 0); // top word + exp=DECCOMBEXP[sourhi>>26]; // get exponent high bits (in place) + + if (EXPISSPECIAL(exp)) { // is special? + // NaNs are handled as usual + if (DFISNAN(df)) return decNaNs(result, df, NULL, set); + // must be infinite; return canonical infinity with sign of df + return decInfinity(result, df); + } + + /* Here when the argument is finite */ + // complete extraction of the exponent + exp+=GETECON(df)-DECBIAS; // .. + continuation and unbias + + if (exp>=0) return decCanonical(result, df); // already integral + + saveround=set->round; // save rounding mode .. + savestatus=set->status; // .. and status + set->round=rmode; // set mode + decFloatZero(&zero); // make 0E+0 + decFloatQuantize(result, df, &zero, set); // 'integrate'; cannot fail + set->round=saveround; // restore rounding mode .. + if (!exact) set->status=savestatus; // .. and status, unless exact + return result; + } // decToIntegral diff --git a/extern/decNumber/decCommon.c b/extern/decNumber/decCommon.c new file mode 100644 index 0000000000..6a0c112c88 --- /dev/null +++ b/extern/decNumber/decCommon.c @@ -0,0 +1,1835 @@ +/* ------------------------------------------------------------------ */ +/* decCommon.c -- common code for all three fixed-size types */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises code that is shared between all the formats */ +/* (decSingle, decDouble, and decQuad); it includes set and extract */ +/* of format components, widening, narrowing, and string conversions. */ +/* */ +/* Unlike decNumber, parameterization takes place at compile time */ +/* rather than at runtime. The parameters are set in the decDouble.c */ +/* (etc.) files, which then include this one to produce the compiled */ +/* code. The functions here, therefore, are code shared between */ +/* multiple formats. */ +/* ------------------------------------------------------------------ */ +// Names here refer to decFloat rather than to decDouble, etc., and +// the functions are in strict alphabetical order. +// Constants, tables, and debug function(s) are included only for QUAD +// (which will always be compiled if DOUBLE or SINGLE are used). +// +// Whenever a decContext is used, only the status may be set (using +// OR) or the rounding mode read; all other fields are ignored and +// untouched. + +// names for simpler testing and default context +#if DECPMAX==7 + #define SINGLE 1 + #define DOUBLE 0 + #define QUAD 0 + #define DEFCONTEXT DEC_INIT_DECIMAL32 +#elif DECPMAX==16 + #define SINGLE 0 + #define DOUBLE 1 + #define QUAD 0 + #define DEFCONTEXT DEC_INIT_DECIMAL64 +#elif DECPMAX==34 + #define SINGLE 0 + #define DOUBLE 0 + #define QUAD 1 + #define DEFCONTEXT DEC_INIT_DECIMAL128 +#else + #error Unexpected DECPMAX value +#endif + +/* Assertions */ + +#if DECPMAX!=7 && DECPMAX!=16 && DECPMAX!=34 + #error Unexpected Pmax (DECPMAX) value for this module +#endif + +// Assert facts about digit characters, etc. +#if ('9'&0x0f)!=9 + #error This module assumes characters are of the form 0b....nnnn + // where .... are don't care 4 bits and nnnn is 0000 through 1001 +#endif +#if ('9'&0xf0)==('.'&0xf0) + #error This module assumes '.' has a different mask than a digit +#endif + +// Assert ToString lay-out conditions +#if DECSTRING DECSTRING + #error Exponent form can be too long for ToString to lay out safely +#endif +#if DECEMAXD > 4 + #error Exponent form is too long for ToString to lay out + // Note: code for up to 9 digits exists in archives [decOct] +#endif + +/* Private functions used here and possibly in decBasic.c, etc. */ +static decFloat * decFinalize(decFloat *, bcdnum *, decContext *); +static Flag decBiStr(const char *, const char *, const char *); + +/* Macros and private tables; those which are not format-dependent */ +/* are only included if decQuad is being built. */ + +/* ------------------------------------------------------------------ */ +/* Combination field lookup tables (uInts to save measurable work) */ +/* */ +/* DECCOMBEXP - 2 most-significant-bits of exponent (00, 01, or */ +/* 10), shifted left for format, or DECFLOAT_Inf/NaN */ +/* DECCOMBWEXP - The same, for the next-wider format (unless QUAD) */ +/* DECCOMBMSD - 4-bit most-significant-digit */ +/* [0 if the index is a special (Infinity or NaN)] */ +/* DECCOMBFROM - 5-bit combination field from EXP top bits and MSD */ +/* (placed in uInt so no shift is needed) */ +/* */ +/* DECCOMBEXP, DECCOMBWEXP, and DECCOMBMSD are indexed by the sign */ +/* and 5-bit combination field (0-63, the second half of the table */ +/* identical to the first half) */ +/* DECCOMBFROM is indexed by expTopTwoBits*16 + msd */ +/* */ +/* DECCOMBMSD and DECCOMBFROM are not format-dependent and so are */ +/* only included once, when QUAD is being built */ +/* ------------------------------------------------------------------ */ +static const uInt DECCOMBEXP[64]={ + 0, 0, 0, 0, 0, 0, 0, 0, + 1< DPD +#define DEC_BIN2DPD 1 // 0-999 -> DPD +#define DEC_BIN2BCD8 1 // 0-999 -> ddd, len +#define DEC_DPD2BCD8 1 // DPD -> ddd, len +#define DEC_DPD2BIN 1 // DPD -> 0-999 +#define DEC_DPD2BINK 1 // DPD -> 0-999000 +#define DEC_DPD2BINM 1 // DPD -> 0-999000000 +#include "decDPD.h" // source of the lookup tables + +#endif + +/* ----------------------------------------------------------------- */ +/* decBiStr -- compare string with pairwise options */ +/* */ +/* targ is the string to compare */ +/* str1 is one of the strings to compare against (length may be 0) */ +/* str2 is the other; it must be the same length as str1 */ +/* */ +/* returns 1 if strings compare equal, (that is, targ is the same */ +/* length as str1 and str2, and each character of targ is in one */ +/* of str1 or str2 in the corresponding position), or 0 otherwise */ +/* */ +/* This is used for generic caseless compare, including the awkward */ +/* case of the Turkish dotted and dotless Is. Use as (for example): */ +/* if (decBiStr(test, "mike", "MIKE")) ... */ +/* ----------------------------------------------------------------- */ +static Flag decBiStr(const char *targ, const char *str1, const char *str2) { + for (;;targ++, str1++, str2++) { + if (*targ!=*str1 && *targ!=*str2) return 0; + // *targ has a match in one (or both, if terminator) + if (*targ=='\0') break; + } // forever + return 1; + } // decBiStr + +/* ------------------------------------------------------------------ */ +/* decFinalize -- adjust and store a final result */ +/* */ +/* df is the decFloat format number which gets the final result */ +/* num is the descriptor of the number to be checked and encoded */ +/* [its values, including the coefficient, may be modified] */ +/* set is the context to use */ +/* returns df */ +/* */ +/* The num descriptor may point to a bcd8 string of any length; this */ +/* string may have leading insignificant zeros. If it has more than */ +/* DECPMAX digits then the final digit can be a round-for-reround */ +/* digit (i.e., it may include a sticky bit residue). */ +/* */ +/* The exponent (q) may be one of the codes for a special value and */ +/* can be up to 999999999 for conversion from string. */ +/* */ +/* No error is possible, but Inexact, Underflow, and/or Overflow may */ +/* be set. */ +/* ------------------------------------------------------------------ */ +// Constant whose size varies with format; also the check for surprises +static uByte allnines[DECPMAX]= +#if SINGLE + {9, 9, 9, 9, 9, 9, 9}; +#elif DOUBLE + {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}; +#elif QUAD + {9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, + 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9}; +#endif + +static decFloat * decFinalize(decFloat *df, bcdnum *num, + decContext *set) { + uByte *ub; // work + uInt dpd; // .. + uInt uiwork; // for macros + uByte *umsd=num->msd; // local copy + uByte *ulsd=num->lsd; // .. + uInt encode; // encoding accumulator + Int length; // coefficient length + + #if DECCHECK + Int clen=ulsd-umsd+1; + #if QUAD + #define COEXTRA 2 // extra-long coefficent + #else + #define COEXTRA 0 + #endif + if (clen<1 || clen>DECPMAX*3+2+COEXTRA) + printf("decFinalize: suspect coefficient [length=%ld]\n", (LI)clen); + if (num->sign!=0 && num->sign!=DECFLOAT_Sign) + printf("decFinalize: bad sign [%08lx]\n", (LI)num->sign); + if (!EXPISSPECIAL(num->exponent) + && (num->exponent>1999999999 || num->exponent<-1999999999)) + printf("decFinalize: improbable exponent [%ld]\n", (LI)num->exponent); + // decShowNum(num, "final"); + #endif + + // A special will have an 'exponent' which is very positive and a + // coefficient < DECPMAX + length=(uInt)(ulsd-umsd+1); // coefficient length + + if (!NUMISSPECIAL(num)) { + Int drop; // digits to be dropped + // skip leading insignificant zeros to calculate an exact length + // [this is quite expensive] + if (*umsd==0) { + for (; umsd+3exponent); + // drop can now be > digits for bottom-clamp (subnormal) cases + if (drop>0) { // rounding needed + // (decFloatQuantize has very similar code to this, so any + // changes may need to be made there, too) + uByte *roundat; // -> re-round digit + uByte reround; // reround value + // printf("Rounding; drop=%ld\n", (LI)drop); + + num->exponent+=drop; // always update exponent + + // Three cases here: + // 1. new LSD is in coefficient (almost always) + // 2. new LSD is digit to left of coefficient (so MSD is + // round-for-reround digit) + // 3. new LSD is to left of case 2 (whole coefficient is sticky) + // [duplicate check-stickies code to save a test] + // [by-digit check for stickies as runs of zeros are rare] + if (dropstatus|=DEC_Inexact; + // if adjusted exponent [exp+digits-1] is < EMIN then num is + // subnormal -- so raise Underflow + if (num->exponentexponent+(ulsd-umsd+1)-1)status|=DEC_Underflow; + + // next decide whether increment of the coefficient is needed + if (set->round==DEC_ROUND_HALF_EVEN) { // fastpath slowest case + if (reround>5) bump=1; // >0.5 goes up + else if (reround==5) // exactly 0.5000 .. + bump=*ulsd & 0x01; // .. up iff [new] lsd is odd + } // r-h-e + else switch (set->round) { + case DEC_ROUND_DOWN: { + // no change + break;} // r-d + case DEC_ROUND_HALF_DOWN: { + if (reround>5) bump=1; + break;} // r-h-d + case DEC_ROUND_HALF_UP: { + if (reround>=5) bump=1; + break;} // r-h-u + case DEC_ROUND_UP: { + if (reround>0) bump=1; + break;} // r-u + case DEC_ROUND_CEILING: { + // same as _UP for positive numbers, and as _DOWN for negatives + if (!num->sign && reround>0) bump=1; + break;} // r-c + case DEC_ROUND_FLOOR: { + // same as _UP for negative numbers, and as _DOWN for positive + // [negative reround cannot occur on 0] + if (num->sign && reround>0) bump=1; + break;} // r-f + case DEC_ROUND_05UP: { + if (reround>0) { // anything out there is 'sticky' + // bump iff lsd=0 or 5; this cannot carry so it could be + // effected immediately with no bump -- but the code + // is clearer if this is done the same way as the others + if (*ulsd==0 || *ulsd==5) bump=1; + } + break;} // r-r + default: { // e.g., DEC_ROUND_MAX + set->status|=DEC_Invalid_context; + #if DECCHECK + printf("Unknown rounding mode: %ld\n", (LI)set->round); + #endif + break;} + } // switch (not r-h-e) + // printf("ReRound: %ld bump: %ld\n", (LI)reround, (LI)bump); + + if (bump!=0) { // need increment + // increment the coefficient; this might end up with 1000... + // (after the all nines case) + ub=ulsd; + for(; ub-3>=umsd && UBTOUI(ub-3)==0x09090909; ub-=4) { + UBFROMUI(ub-3, 0); // to 00000000 + } + // [note ub could now be to left of msd, and it is not safe + // to write to the the left of the msd] + // now at most 3 digits left to non-9 (usually just the one) + for (; ub>=umsd; *ub=0, ub--) { + if (*ub==9) continue; // carry + *ub+=1; + break; + } + if (ubexponent++; + } + else { + // if coefficient is shorter than Pmax then num is + // subnormal, so extend it; this is safe as drop>0 + // (or, if the coefficient was supplied above, it could + // not be 9); this may make the result normal. + ulsd++; + *ulsd=0; + // [exponent unchanged] + #if DECCHECK + if (num->exponent!=DECQTINY) // sanity check + printf("decFinalize: bad all-nines extend [^%ld, %ld]\n", + (LI)num->exponent, (LI)(ulsd-umsd+1)); + #endif + } // subnormal extend + } // had all-nines + } // bump needed + } // inexact rounding + + length=ulsd-umsd+1; // recalculate (may be 0) + + // The coefficient will now fit and has final length unless overflow + // decShowNum(num, "rounded"); + + // if exponent is >=emax may have to clamp, overflow, or fold-down + if (num->exponent>DECEMAX-(DECPMAX-1)) { // is edge case + // printf("overflow checks...\n"); + if (*ulsd==0 && ulsd==umsd) { // have zero + num->exponent=DECEMAX-(DECPMAX-1); // clamp to max + } + else if ((num->exponent+length-1)>DECEMAX) { // > Nmax + // Overflow -- these could go straight to encoding, here, but + // instead num is adjusted to keep the code cleaner + Flag needmax=0; // 1 for finite result + set->status|=(DEC_Overflow | DEC_Inexact); + switch (set->round) { + case DEC_ROUND_DOWN: { + needmax=1; // never Infinity + break;} // r-d + case DEC_ROUND_05UP: { + needmax=1; // never Infinity + break;} // r-05 + case DEC_ROUND_CEILING: { + if (num->sign) needmax=1; // Infinity iff non-negative + break;} // r-c + case DEC_ROUND_FLOOR: { + if (!num->sign) needmax=1; // Infinity iff negative + break;} // r-f + default: break; // Infinity in all other cases + } + if (!needmax) { // easy .. set Infinity + num->exponent=DECFLOAT_Inf; + *umsd=0; // be clean: coefficient to 0 + ulsd=umsd; // .. + } + else { // return Nmax + umsd=allnines; // use constant array + ulsd=allnines+DECPMAX-1; + num->exponent=DECEMAX-(DECPMAX-1); + } + } + else { // no overflow but non-zero and may have to fold-down + Int shift=num->exponent-(DECEMAX-(DECPMAX-1)); + if (shift>0) { // fold-down needed + // fold down needed; must copy to buffer in order to pad + // with zeros safely; fortunately this is not the worst case + // path because cannot have had a round + uByte buffer[ROUNDUP(DECPMAX+3, 4)]; // [+3 allows uInt padding] + uByte *s=umsd; // source + uByte *t=buffer; // safe target + uByte *tlsd=buffer+(ulsd-umsd)+shift; // target LSD + // printf("folddown shift=%ld\n", (LI)shift); + for (; s<=ulsd; s+=4, t+=4) UBFROMUI(t, UBTOUI(s)); + for (t=tlsd-shift+1; t<=tlsd; t+=4) UBFROMUI(t, 0); // pad 0s + num->exponent-=shift; + umsd=buffer; + ulsd=tlsd; + } + } // fold-down? + length=ulsd-umsd+1; // recalculate length + } // high-end edge case + } // finite number + + /*------------------------------------------------------------------*/ + /* At this point the result will properly fit the decFloat */ + /* encoding, and it can be encoded with no possibility of error */ + /*------------------------------------------------------------------*/ + // Following code does not alter coefficient (could be allnines array) + + // fast path possible when DECPMAX digits + if (length==DECPMAX) { + return decFloatFromBCD(df, num->exponent, umsd, num->sign); + } // full-length + + // slower path when not a full-length number; must care about length + // [coefficient length here will be < DECPMAX] + if (!NUMISSPECIAL(num)) { // is still finite + // encode the combination field and exponent continuation + uInt uexp=(uInt)(num->exponent+DECBIAS); // biased exponent + uInt code=(uexp>>DECECONL)<<4; // top two bits of exp + // [msd==0] + // look up the combination field and make high word + encode=DECCOMBFROM[code]; // indexed by (0-2)*16+msd + encode|=(uexp<<(32-6-DECECONL)) & 0x03ffffff; // exponent continuation + } + else encode=num->exponent; // special [already in word] + encode|=num->sign; // add sign + + // private macro to extract a declet, n (where 0<=n=umsd) dpd=BCD2DPD[(*ub*256)+(*(ub+1)*16)+*(ub+2)]; \ + else {dpd=*(ub+2); if (ub+1==umsd) dpd+=*(ub+1)*16; dpd=BCD2DPD[dpd];} + + // place the declets in the encoding words and copy to result (df), + // according to endianness; in all cases complete the sign word + // first + #if DECPMAX==7 + getDPDt(dpd, 1); + encode|=dpd<<10; + getDPDt(dpd, 0); + encode|=dpd; + DFWORD(df, 0)=encode; // just the one word + + #elif DECPMAX==16 + getDPDt(dpd, 4); encode|=dpd<<8; + getDPDt(dpd, 3); encode|=dpd>>2; + DFWORD(df, 0)=encode; + encode=dpd<<30; + getDPDt(dpd, 2); encode|=dpd<<20; + getDPDt(dpd, 1); encode|=dpd<<10; + getDPDt(dpd, 0); encode|=dpd; + DFWORD(df, 1)=encode; + + #elif DECPMAX==34 + getDPDt(dpd,10); encode|=dpd<<4; + getDPDt(dpd, 9); encode|=dpd>>6; + DFWORD(df, 0)=encode; + + encode=dpd<<26; + getDPDt(dpd, 8); encode|=dpd<<16; + getDPDt(dpd, 7); encode|=dpd<<6; + getDPDt(dpd, 6); encode|=dpd>>4; + DFWORD(df, 1)=encode; + + encode=dpd<<28; + getDPDt(dpd, 5); encode|=dpd<<18; + getDPDt(dpd, 4); encode|=dpd<<8; + getDPDt(dpd, 3); encode|=dpd>>2; + DFWORD(df, 2)=encode; + + encode=dpd<<30; + getDPDt(dpd, 2); encode|=dpd<<20; + getDPDt(dpd, 1); encode|=dpd<<10; + getDPDt(dpd, 0); encode|=dpd; + DFWORD(df, 3)=encode; + #endif + + // printf("Status: %08lx\n", (LI)set->status); + // decFloatShow(df, "final2"); + return df; + } // decFinalize + +/* ------------------------------------------------------------------ */ +/* decFloatFromBCD -- set decFloat from exponent, BCD8, and sign */ +/* */ +/* df is the target decFloat */ +/* exp is the in-range unbiased exponent, q, or a special value in */ +/* the form returned by decFloatGetExponent */ +/* bcdar holds DECPMAX digits to set the coefficient from, one */ +/* digit in each byte (BCD8 encoding); the first (MSD) is ignored */ +/* if df is a NaN; all are ignored if df is infinite. */ +/* All bytes must be in 0-9; results are undefined otherwise. */ +/* sig is DECFLOAT_Sign to set the sign bit, 0 otherwise */ +/* returns df, which will be canonical */ +/* */ +/* No error is possible, and no status will be set. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatFromBCD(decFloat *df, Int exp, const uByte *bcdar, + Int sig) { + uInt encode, dpd; // work + const uByte *ub; // .. + + if (EXPISSPECIAL(exp)) encode=exp|sig;// specials already encoded + else { // is finite + // encode the combination field and exponent continuation + uInt uexp=(uInt)(exp+DECBIAS); // biased exponent + uInt code=(uexp>>DECECONL)<<4; // top two bits of exp + code+=bcdar[0]; // add msd + // look up the combination field and make high word + encode=DECCOMBFROM[code]|sig; // indexed by (0-2)*16+msd + encode|=(uexp<<(32-6-DECECONL)) & 0x03ffffff; // exponent continuation + } + + // private macro to extract a declet, n (where 0<=n>2; + DFWORD(df, 0)=encode; + encode=dpd<<30; + getDPDb(dpd, 2); encode|=dpd<<20; + getDPDb(dpd, 1); encode|=dpd<<10; + getDPDb(dpd, 0); encode|=dpd; + DFWORD(df, 1)=encode; + + #elif DECPMAX==34 + getDPDb(dpd,10); encode|=dpd<<4; + getDPDb(dpd, 9); encode|=dpd>>6; + DFWORD(df, 0)=encode; + + encode=dpd<<26; + getDPDb(dpd, 8); encode|=dpd<<16; + getDPDb(dpd, 7); encode|=dpd<<6; + getDPDb(dpd, 6); encode|=dpd>>4; + DFWORD(df, 1)=encode; + + encode=dpd<<28; + getDPDb(dpd, 5); encode|=dpd<<18; + getDPDb(dpd, 4); encode|=dpd<<8; + getDPDb(dpd, 3); encode|=dpd>>2; + DFWORD(df, 2)=encode; + + encode=dpd<<30; + getDPDb(dpd, 2); encode|=dpd<<20; + getDPDb(dpd, 1); encode|=dpd<<10; + getDPDb(dpd, 0); encode|=dpd; + DFWORD(df, 3)=encode; + #endif + // decFloatShow(df, "fromB"); + return df; + } // decFloatFromBCD + +/* ------------------------------------------------------------------ */ +/* decFloatFromPacked -- set decFloat from exponent and packed BCD */ +/* */ +/* df is the target decFloat */ +/* exp is the in-range unbiased exponent, q, or a special value in */ +/* the form returned by decFloatGetExponent */ +/* packed holds DECPMAX packed decimal digits plus a sign nibble */ +/* (all 6 codes are OK); the first (MSD) is ignored if df is a NaN */ +/* and all except sign are ignored if df is infinite. For DOUBLE */ +/* and QUAD the first (pad) nibble is also ignored in all cases. */ +/* All coefficient nibbles must be in 0-9 and sign in A-F; results */ +/* are undefined otherwise. */ +/* returns df, which will be canonical */ +/* */ +/* No error is possible, and no status will be set. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatFromPacked(decFloat *df, Int exp, const uByte *packed) { + uByte bcdar[DECPMAX+2]; // work [+1 for pad, +1 for sign] + const uByte *ip; // .. + uByte *op; // .. + Int sig=0; // sign + + // expand coefficient and sign to BCDAR + #if SINGLE + op=bcdar+1; // no pad digit + #else + op=bcdar; // first (pad) digit ignored + #endif + for (ip=packed; ip>4; + *op++=(uByte)(*ip&0x0f); // [final nibble is sign] + } + op--; // -> sign byte + if (*op==DECPMINUS || *op==DECPMINUSALT) sig=DECFLOAT_Sign; + + if (EXPISSPECIAL(exp)) { // Infinity or NaN + if (!EXPISINF(exp)) bcdar[1]=0; // a NaN: ignore MSD + else memset(bcdar+1, 0, DECPMAX); // Infinite: coefficient to 0 + } + return decFloatFromBCD(df, exp, bcdar+1, sig); + } // decFloatFromPacked + +/* ------------------------------------------------------------------ */ +/* decFloatFromPackedChecked -- set from exponent and packed; checked */ +/* */ +/* df is the target decFloat */ +/* exp is the in-range unbiased exponent, q, or a special value in */ +/* the form returned by decFloatGetExponent */ +/* packed holds DECPMAX packed decimal digits plus a sign nibble */ +/* (all 6 codes are OK); the first (MSD) must be 0 if df is a NaN */ +/* and all digits must be 0 if df is infinite. For DOUBLE and */ +/* QUAD the first (pad) nibble must be 0. */ +/* All coefficient nibbles must be in 0-9 and sign in A-F. */ +/* returns df, which will be canonical or NULL if any of the */ +/* requirements are not met (if this case df is unchanged); that */ +/* is, the input data must be as returned by decFloatToPacked, */ +/* except that all six sign codes are acccepted. */ +/* */ +/* No status will be set. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatFromPackedChecked(decFloat *df, Int exp, + const uByte *packed) { + uByte bcdar[DECPMAX+2]; // work [+1 for pad, +1 for sign] + const uByte *ip; // .. + uByte *op; // .. + Int sig=0; // sign + + // expand coefficient and sign to BCDAR + #if SINGLE + op=bcdar+1; // no pad digit + #else + op=bcdar; // first (pad) digit here + #endif + for (ip=packed; ip>4; + if (*op>9) return NULL; + op++; + *op=(uByte)(*ip&0x0f); // [final nibble is sign] + if (*op>9 && ip sign byte + if (*op<=9) return NULL; // bad sign + if (*op==DECPMINUS || *op==DECPMINUSALT) sig=DECFLOAT_Sign; + + #if !SINGLE + if (bcdar[0]!=0) return NULL; // bad pad nibble + #endif + + if (EXPISNAN(exp)) { // a NaN + if (bcdar[1]!=0) return NULL; // bad msd + } // NaN + else if (EXPISINF(exp)) { // is infinite + Int i; + for (i=0; iDECEMAX-DECPMAX+1) return NULL; + if (exp first character of decimal part + const char *c; // work + uByte *ub; // .. + uInt uiwork; // for macros + bcdnum num; // collects data for finishing + uInt error=DEC_Conversion_syntax; // assume the worst + uByte buffer[ROUNDUP(DECSTRING+11, 8)]; // room for most coefficents, + // some common rounding, +3, & pad + #if DECTRACE + // printf("FromString %s ...\n", string); + #endif + + for(;;) { // once-only 'loop' + num.sign=0; // assume non-negative + num.msd=buffer; // MSD is here always + + // detect and validate the coefficient, including any leading, + // trailing, or embedded '.' + // [could test four-at-a-time here (saving 10% for decQuads), + // but that risks storage violation because the position of the + // terminator is unknown] + for (c=string;; c++) { // -> input character + if (((unsigned)(*c-'0'))<=9) continue; // '0' through '9' is good + if (*c=='\0') break; // most common non-digit + if (*c=='.') { + if (dotchar!=NULL) break; // not first '.' + dotchar=c; // record offset into decimal part + continue;} + if (c==string) { // first in string... + if (*c=='-') { // valid - sign + cfirst++; + num.sign=DECFLOAT_Sign; + continue;} + if (*c=='+') { // valid + sign + cfirst++; + continue;} + } + // *c is not a digit, terminator, or a valid +, -, or '.' + break; + } // c loop + + digits=(uInt)(c-cfirst); // digits (+1 if a dot) + + if (digits>0) { // had digits and/or dot + const char *clast=c-1; // note last coefficient char position + Int exp=0; // exponent accumulator + if (*c!='\0') { // something follows the coefficient + uInt edig; // unsigned work + // had some digits and more to come; expect E[+|-]nnn now + const char *firstexp; // exponent first non-zero + if (*c!='E' && *c!='e') break; + c++; // to (optional) sign + if (*c=='-' || *c=='+') c++; // step over sign (c=clast+2) + if (*c=='\0') break; // no digits! (e.g., '1.2E') + for (; *c=='0';) c++; // skip leading zeros [even last] + firstexp=c; // remember start [maybe '\0'] + // gather exponent digits + edig=(uInt)*c-(uInt)'0'; + if (edig<=9) { // [check not bad or terminator] + exp+=edig; // avoid initial X10 + c++; + for (;; c++) { + edig=(uInt)*c-(uInt)'0'; + if (edig>9) break; + exp=exp*10+edig; + } + } + // if not now on the '\0', *c must not be a digit + if (*c!='\0') break; + + // (this next test must be after the syntax checks) + // if definitely more than the possible digits for format then + // the exponent may have wrapped, so simply set it to a certain + // over/underflow value + if (c>firstexp+DECEMAXD) exp=DECEMAX*2; + if (*(clast+2)=='-') exp=-exp; // was negative + } // exponent part + + if (dotchar!=NULL) { // had a '.' + digits--; // remove from digits count + if (digits==0) break; // was dot alone: bad syntax + exp-=(Int)(clast-dotchar); // adjust exponent + // [the '.' can now be ignored] + } + num.exponent=exp; // exponent is good; store it + + // Here when whole string has been inspected and syntax is good + // cfirst->first digit or dot, clast->last digit or dot + error=0; // no error possible now + + // if the number of digits in the coefficient will fit in buffer + // then it can simply be converted to bcd8 and copied -- decFinalize + // will take care of leading zeros and rounding; the buffer is big + // enough for all canonical coefficients, including 0.00000nn... + ub=buffer; + if (digits<=(Int)(sizeof(buffer)-3)) { // [-3 allows by-4s copy] + c=cfirst; + if (dotchar!=NULL) { // a dot to worry about + if (*(c+1)=='.') { // common canonical case + *ub++=(uByte)(*c-'0'); // copy leading digit + c+=2; // prepare to handle rest + } + else for (; c<=clast;) { // '.' could be anywhere + // as usual, go by fours when safe; NB it has been asserted + // that a '.' does not have the same mask as a digit + if (c<=clast-3 // safe for four + && (UBTOUI(c)&0xf0f0f0f0)==CHARMASK) { // test four + UBFROMUI(ub, UBTOUI(c)&0x0f0f0f0f); // to BCD8 + ub+=4; + c+=4; + continue; + } + if (*c=='.') { // found the dot + c++; // step over it .. + break; // .. and handle the rest + } + *ub++=(uByte)(*c++-'0'); + } + } // had dot + // Now no dot; do this by fours (where safe) + for (; c<=clast-3; c+=4, ub+=4) UBFROMUI(ub, UBTOUI(c)&0x0f0f0f0f); + for (; c<=clast; c++, ub++) *ub=(uByte)(*c-'0'); + num.lsd=buffer+digits-1; // record new LSD + } // fits + + else { // too long for buffer + // [This is a rare and unusual case; arbitrary-length input] + // strip leading zeros [but leave final 0 if all 0's] + if (*cfirst=='.') cfirst++; // step past dot at start + if (*cfirst=='0') { // [cfirst always -> digit] + for (; cfirst LSD + for (; c<=clast; c++) { // inspect remaining chars + if (*c!='0') { // sticky bit needed + if (*c=='.') continue; // [ignore] + *ub=DECSTICKYTAB[*ub]; // update round-for-reround + break; // no need to look at more + } + } + num.lsd=ub; // record LSD + // adjust exponent for dropped digits + num.exponent+=digits-(Int)(ub-buffer+1); + } // too long for buffer + } // digits and/or dot + + else { // no digits or dot were found + // only Infinities and NaNs are allowed, here + if (*c=='\0') break; // nothing there is bad + buffer[0]=0; // default a coefficient of 0 + num.lsd=buffer; // .. + if (decBiStr(c, "infinity", "INFINITY") + || decBiStr(c, "inf", "INF")) num.exponent=DECFLOAT_Inf; + else { // should be a NaN + num.exponent=DECFLOAT_qNaN; // assume quiet NaN + if (*c=='s' || *c=='S') { // probably an sNaN + num.exponent=DECFLOAT_sNaN; // effect the 's' + c++; // and step over it + } + if (*c!='N' && *c!='n') break; // check caseless "NaN" + c++; + if (*c!='a' && *c!='A') break; // .. + c++; + if (*c!='N' && *c!='n') break; // .. + c++; + // now either nothing, or nnnn payload (no dots), expected + // -> start of integer, and skip leading 0s [including plain 0] + for (cfirst=c; *cfirst=='0';) cfirst++; + if (*cfirst!='\0') { // not empty or all-0, payload + // payload found; check all valid digits and copy to buffer as bcd8 + ub=buffer; + for (c=cfirst;; c++, ub++) { + if ((unsigned)(*c-'0')>9) break; // quit if not 0-9 + if (c-cfirst==DECPMAX-1) break; // too many digits + *ub=(uByte)(*c-'0'); // good bcd8 + } + if (*c!='\0') break; // not all digits, or too many + num.lsd=ub-1; // record new LSD + } + } // NaN or sNaN + error=0; // syntax is OK + } // digits=0 (special expected) + break; // drop out + } // [for(;;) once-loop] + + // decShowNum(&num, "fromStr"); + + if (error!=0) { + set->status|=error; + num.exponent=DECFLOAT_qNaN; // set up quiet NaN + num.sign=0; // .. with 0 sign + buffer[0]=0; // .. and coefficient + num.lsd=buffer; // .. + // decShowNum(&num, "oops"); + } + + // decShowNum(&num, "dffs"); + decFinalize(result, &num, set); // round, check, and lay out + // decFloatShow(result, "fromString"); + return result; + } // decFloatFromString + +/* ------------------------------------------------------------------ */ +/* decFloatFromWider -- conversion from next-wider format */ +/* */ +/* result is the decFloat format number which gets the result of */ +/* the conversion */ +/* wider is the decFloatWider format number which will be narrowed */ +/* set is the context */ +/* returns result */ +/* */ +/* Narrowing can cause rounding, overflow, etc., but not Invalid */ +/* operation (sNaNs are copied and do not signal). */ +/* ------------------------------------------------------------------ */ +// narrow-to is not possible for decQuad format numbers; simply omit +#if !QUAD +decFloat * decFloatFromWider(decFloat *result, const decFloatWider *wider, + decContext *set) { + bcdnum num; // collects data for finishing + uByte bcdar[DECWPMAX]; // room for wider coefficient + uInt widerhi=DFWWORD(wider, 0); // top word + Int exp; + + GETWCOEFF(wider, bcdar); + + num.msd=bcdar; // MSD is here always + num.lsd=bcdar+DECWPMAX-1; // LSD is here always + num.sign=widerhi&0x80000000; // extract sign [DECFLOAT_Sign=Neg] + + // decode the wider combination field to exponent + exp=DECCOMBWEXP[widerhi>>26]; // decode from wider combination field + // if it is a special there's nothing to do unless sNaN; if it's + // finite then add the (wider) exponent continuation and unbias + if (EXPISSPECIAL(exp)) exp=widerhi&0x7e000000; // include sNaN selector + else exp+=GETWECON(wider)-DECWBIAS; + num.exponent=exp; + + // decShowNum(&num, "dffw"); + return decFinalize(result, &num, set);// round, check, and lay out + } // decFloatFromWider +#endif + +/* ------------------------------------------------------------------ */ +/* decFloatGetCoefficient -- get coefficient as BCD8 */ +/* */ +/* df is the decFloat from which to extract the coefficient */ +/* bcdar is where DECPMAX bytes will be written, one BCD digit in */ +/* each byte (BCD8 encoding); if df is a NaN the first byte will */ +/* be zero, and if it is infinite they will all be zero */ +/* returns the sign of the coefficient (DECFLOAT_Sign if negative, */ +/* 0 otherwise) */ +/* */ +/* No error is possible, and no status will be set. If df is a */ +/* special value the array is set to zeros (for Infinity) or to the */ +/* payload of a qNaN or sNaN. */ +/* ------------------------------------------------------------------ */ +Int decFloatGetCoefficient(const decFloat *df, uByte *bcdar) { + if (DFISINF(df)) memset(bcdar, 0, DECPMAX); + else { + GETCOEFF(df, bcdar); // use macro + if (DFISNAN(df)) bcdar[0]=0; // MSD needs correcting + } + return GETSIGN(df); + } // decFloatGetCoefficient + +/* ------------------------------------------------------------------ */ +/* decFloatGetExponent -- get unbiased exponent */ +/* */ +/* df is the decFloat from which to extract the exponent */ +/* returns the exponent, q. */ +/* */ +/* No error is possible, and no status will be set. If df is a */ +/* special value the first seven bits of the decFloat are returned, */ +/* left adjusted and with the first (sign) bit set to 0 (followed by */ +/* 25 0 bits). e.g., -sNaN would return 0x7e000000 (DECFLOAT_sNaN). */ +/* ------------------------------------------------------------------ */ +Int decFloatGetExponent(const decFloat *df) { + if (DFISSPECIAL(df)) return DFWORD(df, 0)&0x7e000000; + return GETEXPUN(df); + } // decFloatGetExponent + +/* ------------------------------------------------------------------ */ +/* decFloatSetCoefficient -- set coefficient from BCD8 */ +/* */ +/* df is the target decFloat (and source of exponent/special value) */ +/* bcdar holds DECPMAX digits to set the coefficient from, one */ +/* digit in each byte (BCD8 encoding); the first (MSD) is ignored */ +/* if df is a NaN; all are ignored if df is infinite. */ +/* sig is DECFLOAT_Sign to set the sign bit, 0 otherwise */ +/* returns df, which will be canonical */ +/* */ +/* No error is possible, and no status will be set. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatSetCoefficient(decFloat *df, const uByte *bcdar, + Int sig) { + uInt exp; // for exponent + uByte bcdzero[DECPMAX]; // for infinities + + // Exponent/special code is extracted from df + if (DFISSPECIAL(df)) { + exp=DFWORD(df, 0)&0x7e000000; + if (DFISINF(df)) { + memset(bcdzero, 0, DECPMAX); + return decFloatFromBCD(df, exp, bcdzero, sig); + } + } + else exp=GETEXPUN(df); + return decFloatFromBCD(df, exp, bcdar, sig); + } // decFloatSetCoefficient + +/* ------------------------------------------------------------------ */ +/* decFloatSetExponent -- set exponent or special value */ +/* */ +/* df is the target decFloat (and source of coefficient/payload) */ +/* set is the context for reporting status */ +/* exp is the unbiased exponent, q, or a special value in the form */ +/* returned by decFloatGetExponent */ +/* returns df, which will be canonical */ +/* */ +/* No error is possible, but Overflow or Underflow might occur. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatSetExponent(decFloat *df, decContext *set, Int exp) { + uByte bcdcopy[DECPMAX]; // for coefficient + bcdnum num; // work + num.exponent=exp; + num.sign=decFloatGetCoefficient(df, bcdcopy); // extract coefficient + if (DFISSPECIAL(df)) { // MSD or more needs correcting + if (DFISINF(df)) memset(bcdcopy, 0, DECPMAX); + bcdcopy[0]=0; + } + num.msd=bcdcopy; + num.lsd=bcdcopy+DECPMAX-1; + return decFinalize(df, &num, set); + } // decFloatSetExponent + +/* ------------------------------------------------------------------ */ +/* decFloatRadix -- returns the base (10) */ +/* */ +/* df is any decFloat of this format */ +/* ------------------------------------------------------------------ */ +uInt decFloatRadix(const decFloat *df) { + if (df) return 10; // to placate compiler + return 10; + } // decFloatRadix + +/* The following function is not available if DECPRINT=0 */ +#if DECPRINT +/* ------------------------------------------------------------------ */ +/* decFloatShow -- printf a decFloat in hexadecimal and decimal */ +/* df is the decFloat to show */ +/* tag is a tag string displayed with the number */ +/* */ +/* This is a debug aid; the precise format of the string may change. */ +/* ------------------------------------------------------------------ */ +void decFloatShow(const decFloat *df, const char *tag) { + char hexbuf[DECBYTES*2+DECBYTES/4+1]; // NB blank after every fourth + char buff[DECSTRING]; // for value in decimal + Int i, j=0; + + for (i=0; ibytes[DECBYTES-1-i]); + #else + sprintf(&hexbuf[j], "%02x", df->bytes[i]); + #endif + j+=2; + // the next line adds blank (and terminator) after final pair, too + if ((i+1)%4==0) {strcpy(&hexbuf[j], " "); j++;} + } + decFloatToString(df, buff); + printf(">%s> %s [big-endian] %s\n", tag, hexbuf, buff); + return; + } // decFloatShow +#endif + +/* ------------------------------------------------------------------ */ +/* decFloatToBCD -- get sign, exponent, and BCD8 from a decFloat */ +/* */ +/* df is the source decFloat */ +/* exp will be set to the unbiased exponent, q, or to a special */ +/* value in the form returned by decFloatGetExponent */ +/* bcdar is where DECPMAX bytes will be written, one BCD digit in */ +/* each byte (BCD8 encoding); if df is a NaN the first byte will */ +/* be zero, and if it is infinite they will all be zero */ +/* returns the sign of the coefficient (DECFLOAT_Sign if negative, */ +/* 0 otherwise) */ +/* */ +/* No error is possible, and no status will be set. */ +/* ------------------------------------------------------------------ */ +Int decFloatToBCD(const decFloat *df, Int *exp, uByte *bcdar) { + if (DFISINF(df)) { + memset(bcdar, 0, DECPMAX); + *exp=DFWORD(df, 0)&0x7e000000; + } + else { + GETCOEFF(df, bcdar); // use macro + if (DFISNAN(df)) { + bcdar[0]=0; // MSD needs correcting + *exp=DFWORD(df, 0)&0x7e000000; + } + else { // finite + *exp=GETEXPUN(df); + } + } + return GETSIGN(df); + } // decFloatToBCD + +/* ------------------------------------------------------------------ */ +/* decFloatToEngString -- conversion to numeric string, engineering */ +/* */ +/* df is the decFloat format number to convert */ +/* string is the string where the result will be laid out */ +/* */ +/* string must be at least DECPMAX+9 characters (the worst case is */ +/* "-0.00000nnn...nnn\0", which is as long as the exponent form when */ +/* DECEMAXD<=4); this condition is asserted above */ +/* */ +/* No error is possible, and no status will be set */ +/* ------------------------------------------------------------------ */ +char * decFloatToEngString(const decFloat *df, char *string){ + uInt msd; // coefficient MSD + Int exp; // exponent top two bits or full + uInt comb; // combination field + char *cstart; // coefficient start + char *c; // output pointer in string + char *s, *t; // .. (source, target) + Int pre, e; // work + const uByte *u; // .. + uInt uiwork; // for macros [one compiler needs + // volatile here to avoid bug, but + // that doubles execution time] + + // Source words; macro handles endianness + uInt sourhi=DFWORD(df, 0); // word with sign + #if DECPMAX==16 + uInt sourlo=DFWORD(df, 1); + #elif DECPMAX==34 + uInt sourmh=DFWORD(df, 1); + uInt sourml=DFWORD(df, 2); + uInt sourlo=DFWORD(df, 3); + #endif + + c=string; // where result will go + if (((Int)sourhi)<0) *c++='-'; // handle sign + comb=sourhi>>26; // sign+combination field + msd=DECCOMBMSD[comb]; // decode the combination field + exp=DECCOMBEXP[comb]; // .. + + if (EXPISSPECIAL(exp)) { // special + if (exp==DECFLOAT_Inf) { // infinity + strcpy(c, "Inf"); + strcpy(c+3, "inity"); + return string; // easy + } + if (sourhi&0x02000000) *c++='s'; // sNaN + strcpy(c, "NaN"); // complete word + c+=3; // step past + // quick exit if the payload is zero + #if DECPMAX==7 + if ((sourhi&0x000fffff)==0) return string; + #elif DECPMAX==16 + if (sourlo==0 && (sourhi&0x0003ffff)==0) return string; + #elif DECPMAX==34 + if (sourlo==0 && sourml==0 && sourmh==0 + && (sourhi&0x00003fff)==0) return string; + #endif + // otherwise drop through to add integer; set correct exp etc. + exp=0; msd=0; // setup for following code + } + else { // complete exponent; top two bits are in place + exp+=GETECON(df)-DECBIAS; // .. + continuation and unbias + } + + /* convert the digits of the significand to characters */ + cstart=c; // save start of coefficient + if (msd) *c++=(char)('0'+(char)msd); // non-zero most significant digit + + // Decode the declets. After extracting each declet, it is + // decoded to a 4-uByte sequence by table lookup; the four uBytes + // are the three encoded BCD8 digits followed by a 1-byte length + // (significant digits, except that 000 has length 0). This allows + // us to left-align the first declet with non-zero content, then + // the remaining ones are full 3-char length. Fixed-length copies + // are used because variable-length memcpy causes a subroutine call + // in at least two compilers. (The copies are length 4 for speed + // and are safe because the last item in the array is of length + // three and has the length byte following.) + #define dpd2char(dpdin) u=&DPD2BCD8[((dpdin)&0x3ff)*4]; \ + if (c!=cstart) {UBFROMUI(c, UBTOUI(u)|CHARMASK); c+=3;} \ + else if (*(u+3)) { \ + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); c+=*(u+3);} + + #if DECPMAX==7 + dpd2char(sourhi>>10); // declet 1 + dpd2char(sourhi); // declet 2 + + #elif DECPMAX==16 + dpd2char(sourhi>>8); // declet 1 + dpd2char((sourhi<<2) | (sourlo>>30)); // declet 2 + dpd2char(sourlo>>20); // declet 3 + dpd2char(sourlo>>10); // declet 4 + dpd2char(sourlo); // declet 5 + + #elif DECPMAX==34 + dpd2char(sourhi>>4); // declet 1 + dpd2char((sourhi<<6) | (sourmh>>26)); // declet 2 + dpd2char(sourmh>>16); // declet 3 + dpd2char(sourmh>>6); // declet 4 + dpd2char((sourmh<<4) | (sourml>>28)); // declet 5 + dpd2char(sourml>>18); // declet 6 + dpd2char(sourml>>8); // declet 7 + dpd2char((sourml<<2) | (sourlo>>30)); // declet 8 + dpd2char(sourlo>>20); // declet 9 + dpd2char(sourlo>>10); // declet 10 + dpd2char(sourlo); // declet 11 + #endif + + if (c==cstart) *c++='0'; // all zeros, empty -- make "0" + + if (exp==0) { // integer or NaN case -- easy + *c='\0'; // terminate + return string; + } + /* non-0 exponent */ + + e=0; // assume no E + pre=(Int)(c-cstart)+exp; // length+exp [c->LSD+1] + // [here, pre-exp is the digits count (==1 for zero)] + + if (exp>0 || pre<-5) { // need exponential form + e=pre-1; // calculate E value + pre=1; // assume one digit before '.' + if (e!=0) { // engineering: may need to adjust + Int adj; // adjustment + // The C remainder operator is undefined for negative numbers, so + // a positive remainder calculation must be used here + if (e<0) { + adj=(-e)%3; + if (adj!=0) adj=3-adj; + } + else { // e>0 + adj=e%3; + } + e=e-adj; + // if dealing with zero still produce an exponent which is a + // multiple of three, as expected, but there will only be the + // one zero before the E, still. Otherwise note the padding. + if (!DFISZERO(df)) pre+=adj; + else { // is zero + if (adj!=0) { // 0.00Esnn needed + e=e+3; + pre=-(2-adj); + } + } // zero + } // engineering adjustment + } // exponential form + // printf("e=%ld pre=%ld exp=%ld\n", (LI)e, (LI)pre, (LI)exp); + + /* modify the coefficient, adding 0s, '.', and E+nn as needed */ + if (pre>0) { // ddd.ddd (plain), perhaps with E + // or dd00 padding for engineering + char *dotat=cstart+pre; + if (dotat=dotat; s-=4, t-=4) UBFROMUI(t, UBTOUI(s)); + *dotat='.'; + c++; // length increased by one + } // need dot? + else for (; c0 + else { + /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (may have + E, but only for 0.00E+3 kind of case -- with plenty of spare + space in this case */ + pre=-pre+2; // gap width, including "0." + t=cstart+ROUNDDOWN4(c-cstart)+pre; // preferred first target point + // backoff if too far to the right + if (t>string+DECSTRING-5) t=string+DECSTRING-5; // adjust to fit + // now shift the entire coefficient to the right, being careful not + // to access to the left of string [cannot use memcpy] + for (s=t-pre; s>=string; s-=4, t-=4) UBFROMUI(t, UBTOUI(s)); + // for Quads and Singles there may be a character or two left... + s+=3; // where next would come from + for(; s>=cstart; s--, t--) *(t+3)=*(s); + // now have fill 0. through 0.00000; use overlaps to avoid tests + if (pre>=4) { + memcpy(cstart+pre-4, "0000", 4); + memcpy(cstart, "0.00", 4); + } + else { // 2 or 3 + *(cstart+pre-1)='0'; + memcpy(cstart, "0.", 2); + } + c+=pre; // to end + } + + // finally add the E-part, if needed; it will never be 0, and has + // a maximum length of 3 or 4 digits (asserted above) + if (e!=0) { + memcpy(c, "E+", 2); // starts with E, assume + + c++; + if (e<0) { + *c='-'; // oops, need '-' + e=-e; // uInt, please + } + c++; + // Three-character exponents are easy; 4-character a little trickier + #if DECEMAXD<=3 + u=&BIN2BCD8[e*4]; // -> 3 digits + length byte + // copy fixed 4 characters [is safe], starting at non-zero + // and with character mask to convert BCD to char + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); + c+=*(u+3); // bump pointer appropriately + #elif DECEMAXD==4 + if (e<1000) { // 3 (or fewer) digits case + u=&BIN2BCD8[e*4]; // -> 3 digits + length byte + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); // [as above] + c+=*(u+3); // bump pointer appropriately + } + else { // 4-digits + Int thou=((e>>3)*1049)>>17; // e/1000 + Int rem=e-(1000*thou); // e%1000 + *c++=(char)('0'+(char)thou); // the thousands digit + u=&BIN2BCD8[rem*4]; // -> 3 digits + length byte + UBFROMUI(c, UBTOUI(u)|CHARMASK);// copy fixed 3+1 characters [is safe] + c+=3; // bump pointer, always 3 digits + } + #endif + } + *c='\0'; // terminate + //printf("res %s\n", string); + return string; + } // decFloatToEngString + +/* ------------------------------------------------------------------ */ +/* decFloatToPacked -- convert decFloat to Packed decimal + exponent */ +/* */ +/* df is the source decFloat */ +/* exp will be set to the unbiased exponent, q, or to a special */ +/* value in the form returned by decFloatGetExponent */ +/* packed is where DECPMAX nibbles will be written with the sign as */ +/* final nibble (0x0c for +, 0x0d for -); a NaN has a first nibble */ +/* of zero, and an infinity is all zeros. decDouble and decQuad */ +/* have a additional leading zero nibble, leading to result */ +/* lengths of 4, 9, and 18 bytes. */ +/* returns the sign of the coefficient (DECFLOAT_Sign if negative, */ +/* 0 otherwise) */ +/* */ +/* No error is possible, and no status will be set. */ +/* ------------------------------------------------------------------ */ +Int decFloatToPacked(const decFloat *df, Int *exp, uByte *packed) { + uByte bcdar[DECPMAX+2]; // work buffer + uByte *ip=bcdar, *op=packed; // work pointers + if (DFISINF(df)) { + memset(bcdar, 0, DECPMAX+2); + *exp=DECFLOAT_Inf; + } + else { + GETCOEFF(df, bcdar+1); // use macro + if (DFISNAN(df)) { + bcdar[1]=0; // MSD needs clearing + *exp=DFWORD(df, 0)&0x7e000000; + } + else { // finite + *exp=GETEXPUN(df); + } + } + // now pack; coefficient currently at bcdar+1 + #if SINGLE + ip++; // ignore first byte + #else + *ip=0; // need leading zero + #endif + // set final byte to Packed BCD sign value + bcdar[DECPMAX+1]=(DFISSIGNED(df) ? DECPMINUS : DECPPLUS); + // pack an even number of bytes... + for (; op>26; // sign+combination field + msd=DECCOMBMSD[comb]; // decode the combination field + exp=DECCOMBEXP[comb]; // .. + + if (!EXPISSPECIAL(exp)) { // finite + // complete exponent; top two bits are in place + exp+=GETECON(df)-DECBIAS; // .. + continuation and unbias + } + else { // IS special + if (exp==DECFLOAT_Inf) { // infinity + strcpy(c, "Infinity"); + return string; // easy + } + if (sourhi&0x02000000) *c++='s'; // sNaN + strcpy(c, "NaN"); // complete word + c+=3; // step past + // quick exit if the payload is zero + #if DECPMAX==7 + if ((sourhi&0x000fffff)==0) return string; + #elif DECPMAX==16 + if (sourlo==0 && (sourhi&0x0003ffff)==0) return string; + #elif DECPMAX==34 + if (sourlo==0 && sourml==0 && sourmh==0 + && (sourhi&0x00003fff)==0) return string; + #endif + // otherwise drop through to add integer; set correct exp etc. + exp=0; msd=0; // setup for following code + } + + /* convert the digits of the significand to characters */ + cstart=c; // save start of coefficient + if (msd) *c++=(char)('0'+(char)msd); // non-zero most significant digit + + // Decode the declets. After extracting each declet, it is + // decoded to a 4-uByte sequence by table lookup; the four uBytes + // are the three encoded BCD8 digits followed by a 1-byte length + // (significant digits, except that 000 has length 0). This allows + // us to left-align the first declet with non-zero content, then + // the remaining ones are full 3-char length. Fixed-length copies + // are used because variable-length memcpy causes a subroutine call + // in at least two compilers. (The copies are length 4 for speed + // and are safe because the last item in the array is of length + // three and has the length byte following.) + #define dpd2char(dpdin) u=&DPD2BCD8[((dpdin)&0x3ff)*4]; \ + if (c!=cstart) {UBFROMUI(c, UBTOUI(u)|CHARMASK); c+=3;} \ + else if (*(u+3)) { \ + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); c+=*(u+3);} + + #if DECPMAX==7 + dpd2char(sourhi>>10); // declet 1 + dpd2char(sourhi); // declet 2 + + #elif DECPMAX==16 + dpd2char(sourhi>>8); // declet 1 + dpd2char((sourhi<<2) | (sourlo>>30)); // declet 2 + dpd2char(sourlo>>20); // declet 3 + dpd2char(sourlo>>10); // declet 4 + dpd2char(sourlo); // declet 5 + + #elif DECPMAX==34 + dpd2char(sourhi>>4); // declet 1 + dpd2char((sourhi<<6) | (sourmh>>26)); // declet 2 + dpd2char(sourmh>>16); // declet 3 + dpd2char(sourmh>>6); // declet 4 + dpd2char((sourmh<<4) | (sourml>>28)); // declet 5 + dpd2char(sourml>>18); // declet 6 + dpd2char(sourml>>8); // declet 7 + dpd2char((sourml<<2) | (sourlo>>30)); // declet 8 + dpd2char(sourlo>>20); // declet 9 + dpd2char(sourlo>>10); // declet 10 + dpd2char(sourlo); // declet 11 + #endif + + if (c==cstart) *c++='0'; // all zeros, empty -- make "0" + + //[This fast path is valid but adds 3-5 cycles to worst case length] + //if (exp==0) { // integer or NaN case -- easy + // *c='\0'; // terminate + // return string; + // } + + e=0; // assume no E + pre=(Int)(c-cstart)+exp; // length+exp [c->LSD+1] + // [here, pre-exp is the digits count (==1 for zero)] + + if (exp>0 || pre<-5) { // need exponential form + e=pre-1; // calculate E value + pre=1; // assume one digit before '.' + } // exponential form + + /* modify the coefficient, adding 0s, '.', and E+nn as needed */ + if (pre>0) { // ddd.ddd (plain), perhaps with E + char *dotat=cstart+pre; + if (dotat=dotat; s-=4, t-=4) UBFROMUI(t, UBTOUI(s)); + *dotat='.'; + c++; // length increased by one + } // need dot? + + // finally add the E-part, if needed; it will never be 0, and has + // a maximum length of 3 or 4 digits (asserted above) + if (e!=0) { + memcpy(c, "E+", 2); // starts with E, assume + + c++; + if (e<0) { + *c='-'; // oops, need '-' + e=-e; // uInt, please + } + c++; + // Three-character exponents are easy; 4-character a little trickier + #if DECEMAXD<=3 + u=&BIN2BCD8[e*4]; // -> 3 digits + length byte + // copy fixed 4 characters [is safe], starting at non-zero + // and with character mask to convert BCD to char + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); + c+=*(u+3); // bump pointer appropriately + #elif DECEMAXD==4 + if (e<1000) { // 3 (or fewer) digits case + u=&BIN2BCD8[e*4]; // -> 3 digits + length byte + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); // [as above] + c+=*(u+3); // bump pointer appropriately + } + else { // 4-digits + Int thou=((e>>3)*1049)>>17; // e/1000 + Int rem=e-(1000*thou); // e%1000 + *c++=(char)('0'+(char)thou); // the thousands digit + u=&BIN2BCD8[rem*4]; // -> 3 digits + length byte + UBFROMUI(c, UBTOUI(u)|CHARMASK); // copy fixed 3+1 characters [is safe] + c+=3; // bump pointer, always 3 digits + } + #endif + } + *c='\0'; // add terminator + //printf("res %s\n", string); + return string; + } // pre>0 + + /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */ + // Surprisingly, this is close to being the worst-case path, so the + // shift is done by fours; this is a little tricky because the + // rightmost character to be written must not be beyond where the + // rightmost terminator could be -- so backoff to not touch + // terminator position if need be (this can make exact alignments + // for full Doubles, but in some cases needs care not to access too + // far to the left) + + pre=-pre+2; // gap width, including "0." + t=cstart+ROUNDDOWN4(c-cstart)+pre; // preferred first target point + // backoff if too far to the right + if (t>string+DECSTRING-5) t=string+DECSTRING-5; // adjust to fit + // now shift the entire coefficient to the right, being careful not + // to access to the left of string [cannot use memcpy] + for (s=t-pre; s>=string; s-=4, t-=4) UBFROMUI(t, UBTOUI(s)); + // for Quads and Singles there may be a character or two left... + s+=3; // where next would come from + for(; s>=cstart; s--, t--) *(t+3)=*(s); + // now have fill 0. through 0.00000; use overlaps to avoid tests + if (pre>=4) { + memcpy(cstart+pre-4, "0000", 4); + memcpy(cstart, "0.00", 4); + } + else { // 2 or 3 + *(cstart+pre-1)='0'; + memcpy(cstart, "0.", 2); + } + *(c+pre)='\0'; // terminate + return string; + } // decFloatToString + +/* ------------------------------------------------------------------ */ +/* decFloatToWider -- conversion to next-wider format */ +/* */ +/* source is the decFloat format number which gets the result of */ +/* the conversion */ +/* wider is the decFloatWider format number which will be narrowed */ +/* returns wider */ +/* */ +/* Widening is always exact; no status is set (sNaNs are copied and */ +/* do not signal). The result will be canonical if the source is, */ +/* and may or may not be if the source is not. */ +/* ------------------------------------------------------------------ */ +// widening is not possible for decQuad format numbers; simply omit +#if !QUAD +decFloatWider * decFloatToWider(const decFloat *source, decFloatWider *wider) { + uInt msd; + + /* Construct and copy the sign word */ + if (DFISSPECIAL(source)) { + // copy sign, combination, and first bit of exponent (sNaN selector) + DFWWORD(wider, 0)=DFWORD(source, 0)&0xfe000000; + msd=0; + } + else { // is finite number + uInt exp=GETEXPUN(source)+DECWBIAS; // get unbiased exponent and rebias + uInt code=(exp>>DECWECONL)<<29; // set two bits of exp [msd=0] + code|=(exp<<(32-6-DECWECONL)) & 0x03ffffff; // add exponent continuation + code|=DFWORD(source, 0)&0x80000000; // add sign + DFWWORD(wider, 0)=code; // .. and place top word in wider + msd=GETMSD(source); // get source coefficient MSD [0-9] + } + /* Copy the coefficient and clear any 'unused' words to left */ + #if SINGLE + DFWWORD(wider, 1)=(DFWORD(source, 0)&0x000fffff)|(msd<<20); + #elif DOUBLE + DFWWORD(wider, 2)=(DFWORD(source, 0)&0x0003ffff)|(msd<<18); + DFWWORD(wider, 3)=DFWORD(source, 1); + DFWWORD(wider, 1)=0; + #endif + return wider; + } // decFloatToWider +#endif + +/* ------------------------------------------------------------------ */ +/* decFloatVersion -- return package version string */ +/* */ +/* returns a constant string describing this package */ +/* ------------------------------------------------------------------ */ +const char *decFloatVersion(void) { + return DECVERSION; + } // decFloatVersion + +/* ------------------------------------------------------------------ */ +/* decFloatZero -- set to canonical (integer) zero */ +/* */ +/* df is the decFloat format number to integer +0 (q=0, c=+0) */ +/* returns df */ +/* */ +/* No error is possible, and no status can be set. */ +/* ------------------------------------------------------------------ */ +decFloat * decFloatZero(decFloat *df){ + DFWORD(df, 0)=ZEROWORD; // set appropriate top word + #if DOUBLE || QUAD + DFWORD(df, 1)=0; + #if QUAD + DFWORD(df, 2)=0; + DFWORD(df, 3)=0; + #endif + #endif + // decFloatShow(df, "zero"); + return df; + } // decFloatZero + +/* ------------------------------------------------------------------ */ +/* Private generic function (not format-specific) for development use */ +/* ------------------------------------------------------------------ */ +// This is included once only, for all to use +#if QUAD && (DECCHECK || DECTRACE) + /* ---------------------------------------------------------------- */ + /* decShowNum -- display bcd8 number in debug form */ + /* */ + /* num is the bcdnum to display */ + /* tag is a string to label the display */ + /* ---------------------------------------------------------------- */ + void decShowNum(const bcdnum *num, const char *tag) { + const char *csign="+"; // sign character + uByte *ub; // work + uInt uiwork; // for macros + if (num->sign==DECFLOAT_Sign) csign="-"; + + printf(">%s> ", tag); + if (num->exponent==DECFLOAT_Inf) printf("%sInfinity", csign); + else if (num->exponent==DECFLOAT_qNaN) printf("%sqNaN", csign); + else if (num->exponent==DECFLOAT_sNaN) printf("%ssNaN", csign); + else { // finite + char qbuf[10]; // for right-aligned q + char *c; // work + const uByte *u; // .. + Int e=num->exponent; // .. exponent + strcpy(qbuf, "q="); + c=&qbuf[2]; // where exponent will go + // lay out the exponent + if (e<0) { + *c++='-'; // add '-' + e=-e; // uInt, please + } + #if DECEMAXD>4 + #error Exponent form is too long for ShowNum to lay out + #endif + if (e==0) *c++='0'; // 0-length case + else if (e<1000) { // 3 (or fewer) digits case + u=&BIN2BCD8[e*4]; // -> 3 digits + length byte + UBFROMUI(c, UBTOUI(u+3-*(u+3))|CHARMASK); // [as above] + c+=*(u+3); // bump pointer appropriately + } + else { // 4-digits + Int thou=((e>>3)*1049)>>17; // e/1000 + Int rem=e-(1000*thou); // e%1000 + *c++=(char)('0'+(char)thou); // the thousands digit + u=&BIN2BCD8[rem*4]; // -> 3 digits + length byte + UBFROMUI(c, UBTOUI(u)|CHARMASK); // copy fixed 3+1 characters [is safe] + c+=3; // bump pointer, always 3 digits + } + *c='\0'; // add terminator + printf("%7s c=%s", qbuf, csign); + } + + if (!EXPISSPECIAL(num->exponent) || num->msd!=num->lsd || *num->lsd!=0) { + for (ub=num->msd; ub<=num->lsd; ub++) { // coefficient... + printf("%1x", *ub); + if ((num->lsd-ub)%3==0 && ub!=num->lsd) printf(" "); // 4-space + } + } + printf("\n"); + } // decShowNum +#endif diff --git a/extern/decNumber/decContext.c b/extern/decNumber/decContext.c new file mode 100644 index 0000000000..6db29be034 --- /dev/null +++ b/extern/decNumber/decContext.c @@ -0,0 +1,437 @@ +/* ------------------------------------------------------------------ */ +/* Decimal Context module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2009. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for handling arithmetic */ +/* context structures. */ +/* ------------------------------------------------------------------ */ + +#include // for strcmp +#include // for printf if DECCHECK +#include "decContext.h" // context and base types +#include "decNumberLocal.h" // decNumber local types, etc. + +/* compile-time endian tester [assumes sizeof(Int)>1] */ +static const Int mfcone=1; // constant 1 +static const Flag *mfctop=(const Flag *)&mfcone; // -> top byte +#define LITEND *mfctop // named flag; 1=little-endian + +/* ------------------------------------------------------------------ */ +/* round-for-reround digits */ +/* ------------------------------------------------------------------ */ +const uByte DECSTICKYTAB[10]={1,1,2,3,4,6,6,7,8,9}; /* used if sticky */ + +/* ------------------------------------------------------------------ */ +/* Powers of ten (powers[n]==10**n, 0<=n<=9) */ +/* ------------------------------------------------------------------ */ +const uInt DECPOWERS[10]={1, 10, 100, 1000, 10000, 100000, 1000000, + 10000000, 100000000, 1000000000}; + +/* ------------------------------------------------------------------ */ +/* decContextClearStatus -- clear bits in current status */ +/* */ +/* context is the context structure to be queried */ +/* mask indicates the bits to be cleared (the status bit that */ +/* corresponds to each 1 bit in the mask is cleared) */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decContext *decContextClearStatus(decContext *context, uInt mask) { + context->status&=~mask; + return context; + } // decContextClearStatus + +/* ------------------------------------------------------------------ */ +/* decContextDefault -- initialize a context structure */ +/* */ +/* context is the structure to be initialized */ +/* kind selects the required set of default values, one of: */ +/* DEC_INIT_BASE -- select ANSI X3-274 defaults */ +/* DEC_INIT_DECIMAL32 -- select IEEE 754 defaults, 32-bit */ +/* DEC_INIT_DECIMAL64 -- select IEEE 754 defaults, 64-bit */ +/* DEC_INIT_DECIMAL128 -- select IEEE 754 defaults, 128-bit */ +/* For any other value a valid context is returned, but with */ +/* Invalid_operation set in the status field. */ +/* returns a context structure with the appropriate initial values. */ +/* ------------------------------------------------------------------ */ +decContext * decContextDefault(decContext *context, Int kind) { + // set defaults... + context->digits=9; // 9 digits + context->emax=DEC_MAX_EMAX; // 9-digit exponents + context->emin=DEC_MIN_EMIN; // .. balanced + context->round=DEC_ROUND_HALF_UP; // 0.5 rises + context->traps=DEC_Errors; // all but informational + context->status=0; // cleared + context->clamp=0; // no clamping + #if DECSUBSET + context->extended=0; // cleared + #endif + switch (kind) { + case DEC_INIT_BASE: + // [use defaults] + break; + case DEC_INIT_DECIMAL32: + context->digits=7; // digits + context->emax=96; // Emax + context->emin=-95; // Emin + context->round=DEC_ROUND_HALF_EVEN; // 0.5 to nearest even + context->traps=0; // no traps set + context->clamp=1; // clamp exponents + #if DECSUBSET + context->extended=1; // set + #endif + break; + case DEC_INIT_DECIMAL64: + context->digits=16; // digits + context->emax=384; // Emax + context->emin=-383; // Emin + context->round=DEC_ROUND_HALF_EVEN; // 0.5 to nearest even + context->traps=0; // no traps set + context->clamp=1; // clamp exponents + #if DECSUBSET + context->extended=1; // set + #endif + break; + case DEC_INIT_DECIMAL128: + context->digits=34; // digits + context->emax=6144; // Emax + context->emin=-6143; // Emin + context->round=DEC_ROUND_HALF_EVEN; // 0.5 to nearest even + context->traps=0; // no traps set + context->clamp=1; // clamp exponents + #if DECSUBSET + context->extended=1; // set + #endif + break; + + default: // invalid Kind + // use defaults, and .. + decContextSetStatus(context, DEC_Invalid_operation); // trap + } + + return context;} // decContextDefault + +/* ------------------------------------------------------------------ */ +/* decContextGetRounding -- return current rounding mode */ +/* */ +/* context is the context structure to be queried */ +/* returns the rounding mode */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +enum rounding decContextGetRounding(decContext *context) { + return context->round; + } // decContextGetRounding + +/* ------------------------------------------------------------------ */ +/* decContextGetStatus -- return current status */ +/* */ +/* context is the context structure to be queried */ +/* returns status */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decContextGetStatus(decContext *context) { + return context->status; + } // decContextGetStatus + +/* ------------------------------------------------------------------ */ +/* decContextRestoreStatus -- restore bits in current status */ +/* */ +/* context is the context structure to be updated */ +/* newstatus is the source for the bits to be restored */ +/* mask indicates the bits to be restored (the status bit that */ +/* corresponds to each 1 bit in the mask is set to the value of */ +/* the correspnding bit in newstatus) */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decContext *decContextRestoreStatus(decContext *context, + uInt newstatus, uInt mask) { + context->status&=~mask; // clear the selected bits + context->status|=(mask&newstatus); // or in the new bits + return context; + } // decContextRestoreStatus + +/* ------------------------------------------------------------------ */ +/* decContextSaveStatus -- save bits in current status */ +/* */ +/* context is the context structure to be queried */ +/* mask indicates the bits to be saved (the status bits that */ +/* correspond to each 1 bit in the mask are saved) */ +/* returns the AND of the mask and the current status */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decContextSaveStatus(decContext *context, uInt mask) { + return context->status&mask; + } // decContextSaveStatus + +/* ------------------------------------------------------------------ */ +/* decContextSetRounding -- set current rounding mode */ +/* */ +/* context is the context structure to be updated */ +/* newround is the value which will replace the current mode */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decContext *decContextSetRounding(decContext *context, + enum rounding newround) { + context->round=newround; + return context; + } // decContextSetRounding + +/* ------------------------------------------------------------------ */ +/* decContextSetStatus -- set status and raise trap if appropriate */ +/* */ +/* context is the context structure to be updated */ +/* status is the DEC_ exception code */ +/* returns the context structure */ +/* */ +/* Control may never return from this routine, if there is a signal */ +/* handler and it takes a long jump. */ +/* ------------------------------------------------------------------ */ +decContext * decContextSetStatus(decContext *context, uInt status) { + context->status|=status; + if (status & context->traps) raise(SIGFPE); + return context;} // decContextSetStatus + +/* ------------------------------------------------------------------ */ +/* decContextSetStatusFromString -- set status from a string + trap */ +/* */ +/* context is the context structure to be updated */ +/* string is a string exactly equal to one that might be returned */ +/* by decContextStatusToString */ +/* */ +/* The status bit corresponding to the string is set, and a trap */ +/* is raised if appropriate. */ +/* */ +/* returns the context structure, unless the string is equal to */ +/* DEC_Condition_MU or is not recognized. In these cases NULL is */ +/* returned. */ +/* ------------------------------------------------------------------ */ +decContext * decContextSetStatusFromString(decContext *context, + const char *string) { + if (strcmp(string, DEC_Condition_CS)==0) + return decContextSetStatus(context, DEC_Conversion_syntax); + if (strcmp(string, DEC_Condition_DZ)==0) + return decContextSetStatus(context, DEC_Division_by_zero); + if (strcmp(string, DEC_Condition_DI)==0) + return decContextSetStatus(context, DEC_Division_impossible); + if (strcmp(string, DEC_Condition_DU)==0) + return decContextSetStatus(context, DEC_Division_undefined); + if (strcmp(string, DEC_Condition_IE)==0) + return decContextSetStatus(context, DEC_Inexact); + if (strcmp(string, DEC_Condition_IS)==0) + return decContextSetStatus(context, DEC_Insufficient_storage); + if (strcmp(string, DEC_Condition_IC)==0) + return decContextSetStatus(context, DEC_Invalid_context); + if (strcmp(string, DEC_Condition_IO)==0) + return decContextSetStatus(context, DEC_Invalid_operation); + #if DECSUBSET + if (strcmp(string, DEC_Condition_LD)==0) + return decContextSetStatus(context, DEC_Lost_digits); + #endif + if (strcmp(string, DEC_Condition_OV)==0) + return decContextSetStatus(context, DEC_Overflow); + if (strcmp(string, DEC_Condition_PA)==0) + return decContextSetStatus(context, DEC_Clamped); + if (strcmp(string, DEC_Condition_RO)==0) + return decContextSetStatus(context, DEC_Rounded); + if (strcmp(string, DEC_Condition_SU)==0) + return decContextSetStatus(context, DEC_Subnormal); + if (strcmp(string, DEC_Condition_UN)==0) + return decContextSetStatus(context, DEC_Underflow); + if (strcmp(string, DEC_Condition_ZE)==0) + return context; + return NULL; // Multiple status, or unknown + } // decContextSetStatusFromString + +/* ------------------------------------------------------------------ */ +/* decContextSetStatusFromStringQuiet -- set status from a string */ +/* */ +/* context is the context structure to be updated */ +/* string is a string exactly equal to one that might be returned */ +/* by decContextStatusToString */ +/* */ +/* The status bit corresponding to the string is set; no trap is */ +/* raised. */ +/* */ +/* returns the context structure, unless the string is equal to */ +/* DEC_Condition_MU or is not recognized. In these cases NULL is */ +/* returned. */ +/* ------------------------------------------------------------------ */ +decContext * decContextSetStatusFromStringQuiet(decContext *context, + const char *string) { + if (strcmp(string, DEC_Condition_CS)==0) + return decContextSetStatusQuiet(context, DEC_Conversion_syntax); + if (strcmp(string, DEC_Condition_DZ)==0) + return decContextSetStatusQuiet(context, DEC_Division_by_zero); + if (strcmp(string, DEC_Condition_DI)==0) + return decContextSetStatusQuiet(context, DEC_Division_impossible); + if (strcmp(string, DEC_Condition_DU)==0) + return decContextSetStatusQuiet(context, DEC_Division_undefined); + if (strcmp(string, DEC_Condition_IE)==0) + return decContextSetStatusQuiet(context, DEC_Inexact); + if (strcmp(string, DEC_Condition_IS)==0) + return decContextSetStatusQuiet(context, DEC_Insufficient_storage); + if (strcmp(string, DEC_Condition_IC)==0) + return decContextSetStatusQuiet(context, DEC_Invalid_context); + if (strcmp(string, DEC_Condition_IO)==0) + return decContextSetStatusQuiet(context, DEC_Invalid_operation); + #if DECSUBSET + if (strcmp(string, DEC_Condition_LD)==0) + return decContextSetStatusQuiet(context, DEC_Lost_digits); + #endif + if (strcmp(string, DEC_Condition_OV)==0) + return decContextSetStatusQuiet(context, DEC_Overflow); + if (strcmp(string, DEC_Condition_PA)==0) + return decContextSetStatusQuiet(context, DEC_Clamped); + if (strcmp(string, DEC_Condition_RO)==0) + return decContextSetStatusQuiet(context, DEC_Rounded); + if (strcmp(string, DEC_Condition_SU)==0) + return decContextSetStatusQuiet(context, DEC_Subnormal); + if (strcmp(string, DEC_Condition_UN)==0) + return decContextSetStatusQuiet(context, DEC_Underflow); + if (strcmp(string, DEC_Condition_ZE)==0) + return context; + return NULL; // Multiple status, or unknown + } // decContextSetStatusFromStringQuiet + +/* ------------------------------------------------------------------ */ +/* decContextSetStatusQuiet -- set status without trap */ +/* */ +/* context is the context structure to be updated */ +/* status is the DEC_ exception code */ +/* returns the context structure */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decContext * decContextSetStatusQuiet(decContext *context, uInt status) { + context->status|=status; + return context;} // decContextSetStatusQuiet + +/* ------------------------------------------------------------------ */ +/* decContextStatusToString -- convert status flags to a string */ +/* */ +/* context is a context with valid status field */ +/* */ +/* returns a constant string describing the condition. If multiple */ +/* (or no) flags are set, a generic constant message is returned. */ +/* ------------------------------------------------------------------ */ +const char *decContextStatusToString(const decContext *context) { + Int status=context->status; + + // test the five IEEE first, as some of the others are ambiguous when + // DECEXTFLAG=0 + if (status==DEC_Invalid_operation ) return DEC_Condition_IO; + if (status==DEC_Division_by_zero ) return DEC_Condition_DZ; + if (status==DEC_Overflow ) return DEC_Condition_OV; + if (status==DEC_Underflow ) return DEC_Condition_UN; + if (status==DEC_Inexact ) return DEC_Condition_IE; + + if (status==DEC_Division_impossible ) return DEC_Condition_DI; + if (status==DEC_Division_undefined ) return DEC_Condition_DU; + if (status==DEC_Rounded ) return DEC_Condition_RO; + if (status==DEC_Clamped ) return DEC_Condition_PA; + if (status==DEC_Subnormal ) return DEC_Condition_SU; + if (status==DEC_Conversion_syntax ) return DEC_Condition_CS; + if (status==DEC_Insufficient_storage ) return DEC_Condition_IS; + if (status==DEC_Invalid_context ) return DEC_Condition_IC; + #if DECSUBSET + if (status==DEC_Lost_digits ) return DEC_Condition_LD; + #endif + if (status==0 ) return DEC_Condition_ZE; + return DEC_Condition_MU; // Multiple errors + } // decContextStatusToString + +/* ------------------------------------------------------------------ */ +/* decContextTestEndian -- test whether DECLITEND is set correctly */ +/* */ +/* quiet is 1 to suppress message; 0 otherwise */ +/* returns 0 if DECLITEND is correct */ +/* 1 if DECLITEND is incorrect and should be 1 */ +/* -1 if DECLITEND is incorrect and should be 0 */ +/* */ +/* A message is displayed if the return value is not 0 and quiet==0. */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +Int decContextTestEndian(Flag quiet) { + Int res=0; // optimist + uInt dle=(uInt)DECLITEND; // unsign + if (dle>1) dle=1; // ensure 0 or 1 + + if (LITEND!=DECLITEND) { + if (!quiet) { // always refer to this + #if DECPRINT + const char *adj; + if (LITEND) adj="little"; + else adj="big"; + printf("Warning: DECLITEND is set to %d, but this computer appears to be %s-endian\n", + DECLITEND, adj); + #endif + } + res=(Int)LITEND-dle; + } + return res; + } // decContextTestEndian + +/* ------------------------------------------------------------------ */ +/* decContextTestSavedStatus -- test bits in saved status */ +/* */ +/* oldstatus is the status word to be tested */ +/* mask indicates the bits to be tested (the oldstatus bits that */ +/* correspond to each 1 bit in the mask are tested) */ +/* returns 1 if any of the tested bits are 1, or 0 otherwise */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decContextTestSavedStatus(uInt oldstatus, uInt mask) { + return (oldstatus&mask)!=0; + } // decContextTestSavedStatus + +/* ------------------------------------------------------------------ */ +/* decContextTestStatus -- test bits in current status */ +/* */ +/* context is the context structure to be updated */ +/* mask indicates the bits to be tested (the status bits that */ +/* correspond to each 1 bit in the mask are tested) */ +/* returns 1 if any of the tested bits are 1, or 0 otherwise */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decContextTestStatus(decContext *context, uInt mask) { + return (context->status&mask)!=0; + } // decContextTestStatus + +/* ------------------------------------------------------------------ */ +/* decContextZeroStatus -- clear all status bits */ +/* */ +/* context is the context structure to be updated */ +/* returns context */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decContext *decContextZeroStatus(decContext *context) { + context->status=0; + return context; + } // decContextZeroStatus + diff --git a/extern/decNumber/decContext.h b/extern/decNumber/decContext.h new file mode 100644 index 0000000000..f5dca1d305 --- /dev/null +++ b/extern/decNumber/decContext.h @@ -0,0 +1,254 @@ +/* ------------------------------------------------------------------ */ +/* Decimal Context module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* */ +/* Context variables must always have valid values: */ +/* */ +/* status -- [any bits may be cleared, but not set, by user] */ +/* round -- must be one of the enumerated rounding modes */ +/* */ +/* The following variables are implied for fixed size formats (i.e., */ +/* they are ignored) but should still be set correctly in case used */ +/* with decNumber functions: */ +/* */ +/* clamp -- must be either 0 or 1 */ +/* digits -- must be in the range 1 through 999999999 */ +/* emax -- must be in the range 0 through 999999999 */ +/* emin -- must be in the range 0 through -999999999 */ +/* extended -- must be either 0 or 1 [present only if DECSUBSET] */ +/* traps -- only defined bits may be set */ +/* */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECCONTEXT) + #define DECCONTEXT + #define DECCNAME "decContext" /* Short name */ + #define DECCFULLNAME "Decimal Context Descriptor" /* Verbose name */ + #define DECCAUTHOR "Mike Cowlishaw" /* Who to blame */ + + #if !defined(int32_t) + #include /* C99 standard integers */ + #endif + #include /* for printf, etc. */ + #include /* for traps */ + + /* Extended flags setting -- set this to 0 to use only IEEE flags */ + #if !defined(DECEXTFLAG) + #define DECEXTFLAG 1 /* 1=enable extended flags */ + #endif + + /* Conditional code flag -- set this to 0 for best performance */ + #if !defined(DECSUBSET) + #define DECSUBSET 0 /* 1=enable subset arithmetic */ + #endif + + /* Context for operations, with associated constants */ + enum rounding { + DEC_ROUND_CEILING, /* round towards +infinity */ + DEC_ROUND_UP, /* round away from 0 */ + DEC_ROUND_HALF_UP, /* 0.5 rounds up */ + DEC_ROUND_HALF_EVEN, /* 0.5 rounds to nearest even */ + DEC_ROUND_HALF_DOWN, /* 0.5 rounds down */ + DEC_ROUND_DOWN, /* round towards 0 (truncate) */ + DEC_ROUND_FLOOR, /* round towards -infinity */ + DEC_ROUND_05UP, /* round for reround */ + DEC_ROUND_MAX /* enum must be less than this */ + }; + #define DEC_ROUND_DEFAULT DEC_ROUND_HALF_EVEN; + + typedef struct { + int32_t digits; /* working precision */ + int32_t emax; /* maximum positive exponent */ + int32_t emin; /* minimum negative exponent */ + enum rounding round; /* rounding mode */ + uint32_t traps; /* trap-enabler flags */ + uint32_t status; /* status flags */ + uint8_t clamp; /* flag: apply IEEE exponent clamp */ + #if DECSUBSET + uint8_t extended; /* flag: special-values allowed */ + #endif + } decContext; + + /* Maxima and Minima for context settings */ + #define DEC_MAX_DIGITS 999999999 + #define DEC_MIN_DIGITS 1 + #define DEC_MAX_EMAX 999999999 + #define DEC_MIN_EMAX 0 + #define DEC_MAX_EMIN 0 + #define DEC_MIN_EMIN -999999999 + #define DEC_MAX_MATH 999999 /* max emax, etc., for math funcs. */ + + /* Classifications for decimal numbers, aligned with 754 (note that */ + /* 'normal' and 'subnormal' are meaningful only with a decContext */ + /* or a fixed size format). */ + enum decClass { + DEC_CLASS_SNAN, + DEC_CLASS_QNAN, + DEC_CLASS_NEG_INF, + DEC_CLASS_NEG_NORMAL, + DEC_CLASS_NEG_SUBNORMAL, + DEC_CLASS_NEG_ZERO, + DEC_CLASS_POS_ZERO, + DEC_CLASS_POS_SUBNORMAL, + DEC_CLASS_POS_NORMAL, + DEC_CLASS_POS_INF + }; + /* Strings for the decClasses */ + #define DEC_ClassString_SN "sNaN" + #define DEC_ClassString_QN "NaN" + #define DEC_ClassString_NI "-Infinity" + #define DEC_ClassString_NN "-Normal" + #define DEC_ClassString_NS "-Subnormal" + #define DEC_ClassString_NZ "-Zero" + #define DEC_ClassString_PZ "+Zero" + #define DEC_ClassString_PS "+Subnormal" + #define DEC_ClassString_PN "+Normal" + #define DEC_ClassString_PI "+Infinity" + #define DEC_ClassString_UN "Invalid" + + /* Trap-enabler and Status flags (exceptional conditions), and */ + /* their names. The top byte is reserved for internal use */ + #if DECEXTFLAG + /* Extended flags */ + #define DEC_Conversion_syntax 0x00000001 + #define DEC_Division_by_zero 0x00000002 + #define DEC_Division_impossible 0x00000004 + #define DEC_Division_undefined 0x00000008 + #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */ + #define DEC_Inexact 0x00000020 + #define DEC_Invalid_context 0x00000040 + #define DEC_Invalid_operation 0x00000080 + #if DECSUBSET + #define DEC_Lost_digits 0x00000100 + #endif + #define DEC_Overflow 0x00000200 + #define DEC_Clamped 0x00000400 + #define DEC_Rounded 0x00000800 + #define DEC_Subnormal 0x00001000 + #define DEC_Underflow 0x00002000 + #else + /* IEEE flags only */ + #define DEC_Conversion_syntax 0x00000010 + #define DEC_Division_by_zero 0x00000002 + #define DEC_Division_impossible 0x00000010 + #define DEC_Division_undefined 0x00000010 + #define DEC_Insufficient_storage 0x00000010 /* [when malloc fails] */ + #define DEC_Inexact 0x00000001 + #define DEC_Invalid_context 0x00000010 + #define DEC_Invalid_operation 0x00000010 + #if DECSUBSET + #define DEC_Lost_digits 0x00000000 + #endif + #define DEC_Overflow 0x00000008 + #define DEC_Clamped 0x00000000 + #define DEC_Rounded 0x00000000 + #define DEC_Subnormal 0x00000000 + #define DEC_Underflow 0x00000004 + #endif + + /* IEEE 754 groupings for the flags */ + /* [DEC_Clamped, DEC_Lost_digits, DEC_Rounded, and DEC_Subnormal */ + /* are not in IEEE 754] */ + #define DEC_IEEE_754_Division_by_zero (DEC_Division_by_zero) + #if DECSUBSET + #define DEC_IEEE_754_Inexact (DEC_Inexact | DEC_Lost_digits) + #else + #define DEC_IEEE_754_Inexact (DEC_Inexact) + #endif + #define DEC_IEEE_754_Invalid_operation (DEC_Conversion_syntax | \ + DEC_Division_impossible | \ + DEC_Division_undefined | \ + DEC_Insufficient_storage | \ + DEC_Invalid_context | \ + DEC_Invalid_operation) + #define DEC_IEEE_754_Overflow (DEC_Overflow) + #define DEC_IEEE_754_Underflow (DEC_Underflow) + + /* flags which are normally errors (result is qNaN, infinite, or 0) */ + #define DEC_Errors (DEC_IEEE_754_Division_by_zero | \ + DEC_IEEE_754_Invalid_operation | \ + DEC_IEEE_754_Overflow | DEC_IEEE_754_Underflow) + /* flags which cause a result to become qNaN */ + #define DEC_NaNs DEC_IEEE_754_Invalid_operation + + /* flags which are normally for information only (finite results) */ + #if DECSUBSET + #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact \ + | DEC_Lost_digits) + #else + #define DEC_Information (DEC_Clamped | DEC_Rounded | DEC_Inexact) + #endif + + /* IEEE 854 names (for compatibility with older decNumber versions) */ + #define DEC_IEEE_854_Division_by_zero DEC_IEEE_754_Division_by_zero + #define DEC_IEEE_854_Inexact DEC_IEEE_754_Inexact + #define DEC_IEEE_854_Invalid_operation DEC_IEEE_754_Invalid_operation + #define DEC_IEEE_854_Overflow DEC_IEEE_754_Overflow + #define DEC_IEEE_854_Underflow DEC_IEEE_754_Underflow + + /* Name strings for the exceptional conditions */ + #define DEC_Condition_CS "Conversion syntax" + #define DEC_Condition_DZ "Division by zero" + #define DEC_Condition_DI "Division impossible" + #define DEC_Condition_DU "Division undefined" + #define DEC_Condition_IE "Inexact" + #define DEC_Condition_IS "Insufficient storage" + #define DEC_Condition_IC "Invalid context" + #define DEC_Condition_IO "Invalid operation" + #if DECSUBSET + #define DEC_Condition_LD "Lost digits" + #endif + #define DEC_Condition_OV "Overflow" + #define DEC_Condition_PA "Clamped" + #define DEC_Condition_RO "Rounded" + #define DEC_Condition_SU "Subnormal" + #define DEC_Condition_UN "Underflow" + #define DEC_Condition_ZE "No status" + #define DEC_Condition_MU "Multiple status" + #define DEC_Condition_Length 21 /* length of the longest string, */ + /* including terminator */ + + /* Initialization descriptors, used by decContextDefault */ + #define DEC_INIT_BASE 0 + #define DEC_INIT_DECIMAL32 32 + #define DEC_INIT_DECIMAL64 64 + #define DEC_INIT_DECIMAL128 128 + /* Synonyms */ + #define DEC_INIT_DECSINGLE DEC_INIT_DECIMAL32 + #define DEC_INIT_DECDOUBLE DEC_INIT_DECIMAL64 + #define DEC_INIT_DECQUAD DEC_INIT_DECIMAL128 + + /* decContext routines */ + extern decContext * decContextClearStatus(decContext *, uint32_t); + extern decContext * decContextDefault(decContext *, int32_t); + extern enum rounding decContextGetRounding(decContext *); + extern uint32_t decContextGetStatus(decContext *); + extern decContext * decContextRestoreStatus(decContext *, uint32_t, uint32_t); + extern uint32_t decContextSaveStatus(decContext *, uint32_t); + extern decContext * decContextSetRounding(decContext *, enum rounding); + extern decContext * decContextSetStatus(decContext *, uint32_t); + extern decContext * decContextSetStatusFromString(decContext *, const char *); + extern decContext * decContextSetStatusFromStringQuiet(decContext *, const char *); + extern decContext * decContextSetStatusQuiet(decContext *, uint32_t); + extern const char * decContextStatusToString(const decContext *); + extern int32_t decContextTestEndian(uint8_t); + extern uint32_t decContextTestSavedStatus(uint32_t, uint32_t); + extern uint32_t decContextTestStatus(decContext *, uint32_t); + extern decContext * decContextZeroStatus(decContext *); + +#endif diff --git a/extern/decNumber/decDPD.h b/extern/decNumber/decDPD.h new file mode 100644 index 0000000000..4daad4f842 --- /dev/null +++ b/extern/decNumber/decDPD.h @@ -0,0 +1,1185 @@ +/* ------------------------------------------------------------------------ */ +/* Binary Coded Decimal and Densely Packed Decimal conversion lookup tables */ +/* [Automatically generated -- do not edit. 2008.06.21] */ +/* ------------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */ +/* ------------------------------------------------------------------------ */ +/* For details, see DPDecimal.html on the General Decimal Arithmetic page. */ +/* */ +/* This include file defines several DPD and BCD conversion tables: */ +/* */ +/* uint16_t BCD2DPD[2458]; -- BCD -> DPD (0x999 => 2457) */ +/* uint16_t BIN2DPD[1000]; -- Bin -> DPD (999 => 2457) */ +/* uint8_t BIN2CHAR[4001]; -- Bin -> CHAR (999 => '\3' '9' '9' '9') */ +/* uint8_t BIN2BCD8[4000]; -- Bin -> bytes (999 => 9 9 9 3) */ +/* uint16_t DPD2BCD[1024]; -- DPD -> BCD (0x3FF => 0x999) */ +/* uint16_t DPD2BIN[1024]; -- DPD -> BIN (0x3FF => 999) */ +/* uint32_t DPD2BINK[1024]; -- DPD -> BIN * 1000 (0x3FF => 999000) */ +/* uint32_t DPD2BINM[1024]; -- DPD -> BIN * 1E+6 (0x3FF => 999000000) */ +/* uint8_t DPD2BCD8[4096]; -- DPD -> bytes (x3FF => 9 9 9 3) */ +/* */ +/* In all cases the result (10 bits or 12 bits, or binary) is right-aligned */ +/* in the table entry. BIN2CHAR entries are a single byte length (0 for */ +/* value 0) followed by three digit characters; a trailing terminator is */ +/* included to allow 4-char moves always. BIN2BCD8 and DPD2BCD8 entries */ +/* are similar with the three BCD8 digits followed by a one-byte length */ +/* (again, length=0 for value 0). */ +/* */ +/* To use a table, its name, prefixed with DEC_, must be defined with a */ +/* value of 1 before this header file is included. For example: */ +/* #define DEC_BCD2DPD 1 */ +/* This mechanism allows software to only include tables that are needed. */ +/* ------------------------------------------------------------------------ */ + +#if defined(DEC_BCD2DPD) && DEC_BCD2DPD==1 && !defined(DECBCD2DPD) +#define DECBCD2DPD + +const uint16_t BCD2DPD[2458]={ 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 0, 0, 0, 0, 0, 0, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 0, 0, 0, 0, 0, 0, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 0, 0, 0, 0, 0, + 0, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 0, 0, + 0, 0, 0, 0, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 0, 0, 0, 0, 0, 0, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 0, 0, 0, 0, 0, 0, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 0, 0, 0, 0, 0, 0, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 0, 0, 0, + 0, 0, 0, 10, 11, 42, 43, 74, 75, 106, 107, 78, 79, + 0, 0, 0, 0, 0, 0, 26, 27, 58, 59, 90, 91, 122, + 123, 94, 95, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 0, 0, + 0, 0, 0, 0, 144, 145, 146, 147, 148, 149, 150, 151, 152, + 153, 0, 0, 0, 0, 0, 0, 160, 161, 162, 163, 164, 165, + 166, 167, 168, 169, 0, 0, 0, 0, 0, 0, 176, 177, 178, + 179, 180, 181, 182, 183, 184, 185, 0, 0, 0, 0, 0, 0, + 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 0, 0, 0, + 0, 0, 0, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, + 0, 0, 0, 0, 0, 0, 224, 225, 226, 227, 228, 229, 230, + 231, 232, 233, 0, 0, 0, 0, 0, 0, 240, 241, 242, 243, + 244, 245, 246, 247, 248, 249, 0, 0, 0, 0, 0, 0, 138, + 139, 170, 171, 202, 203, 234, 235, 206, 207, 0, 0, 0, 0, + 0, 0, 154, 155, 186, 187, 218, 219, 250, 251, 222, 223, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 0, 0, 0, 0, 0, 0, + 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 0, 0, 0, + 0, 0, 0, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, + 0, 0, 0, 0, 0, 0, 304, 305, 306, 307, 308, 309, 310, + 311, 312, 313, 0, 0, 0, 0, 0, 0, 320, 321, 322, 323, + 324, 325, 326, 327, 328, 329, 0, 0, 0, 0, 0, 0, 336, + 337, 338, 339, 340, 341, 342, 343, 344, 345, 0, 0, 0, 0, + 0, 0, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 0, + 0, 0, 0, 0, 0, 368, 369, 370, 371, 372, 373, 374, 375, + 376, 377, 0, 0, 0, 0, 0, 0, 266, 267, 298, 299, 330, + 331, 362, 363, 334, 335, 0, 0, 0, 0, 0, 0, 282, 283, + 314, 315, 346, 347, 378, 379, 350, 351, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 0, 0, 0, 0, 0, 0, 400, 401, 402, 403, + 404, 405, 406, 407, 408, 409, 0, 0, 0, 0, 0, 0, 416, + 417, 418, 419, 420, 421, 422, 423, 424, 425, 0, 0, 0, 0, + 0, 0, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 0, + 0, 0, 0, 0, 0, 448, 449, 450, 451, 452, 453, 454, 455, + 456, 457, 0, 0, 0, 0, 0, 0, 464, 465, 466, 467, 468, + 469, 470, 471, 472, 473, 0, 0, 0, 0, 0, 0, 480, 481, + 482, 483, 484, 485, 486, 487, 488, 489, 0, 0, 0, 0, 0, + 0, 496, 497, 498, 499, 500, 501, 502, 503, 504, 505, 0, 0, + 0, 0, 0, 0, 394, 395, 426, 427, 458, 459, 490, 491, 462, + 463, 0, 0, 0, 0, 0, 0, 410, 411, 442, 443, 474, 475, + 506, 507, 478, 479, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 0, + 0, 0, 0, 0, 0, 528, 529, 530, 531, 532, 533, 534, 535, + 536, 537, 0, 0, 0, 0, 0, 0, 544, 545, 546, 547, 548, + 549, 550, 551, 552, 553, 0, 0, 0, 0, 0, 0, 560, 561, + 562, 563, 564, 565, 566, 567, 568, 569, 0, 0, 0, 0, 0, + 0, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 0, 0, + 0, 0, 0, 0, 592, 593, 594, 595, 596, 597, 598, 599, 600, + 601, 0, 0, 0, 0, 0, 0, 608, 609, 610, 611, 612, 613, + 614, 615, 616, 617, 0, 0, 0, 0, 0, 0, 624, 625, 626, + 627, 628, 629, 630, 631, 632, 633, 0, 0, 0, 0, 0, 0, + 522, 523, 554, 555, 586, 587, 618, 619, 590, 591, 0, 0, 0, + 0, 0, 0, 538, 539, 570, 571, 602, 603, 634, 635, 606, 607, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 640, 641, + 642, 643, 644, 645, 646, 647, 648, 649, 0, 0, 0, 0, 0, + 0, 656, 657, 658, 659, 660, 661, 662, 663, 664, 665, 0, 0, + 0, 0, 0, 0, 672, 673, 674, 675, 676, 677, 678, 679, 680, + 681, 0, 0, 0, 0, 0, 0, 688, 689, 690, 691, 692, 693, + 694, 695, 696, 697, 0, 0, 0, 0, 0, 0, 704, 705, 706, + 707, 708, 709, 710, 711, 712, 713, 0, 0, 0, 0, 0, 0, + 720, 721, 722, 723, 724, 725, 726, 727, 728, 729, 0, 0, 0, + 0, 0, 0, 736, 737, 738, 739, 740, 741, 742, 743, 744, 745, + 0, 0, 0, 0, 0, 0, 752, 753, 754, 755, 756, 757, 758, + 759, 760, 761, 0, 0, 0, 0, 0, 0, 650, 651, 682, 683, + 714, 715, 746, 747, 718, 719, 0, 0, 0, 0, 0, 0, 666, + 667, 698, 699, 730, 731, 762, 763, 734, 735, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 768, 769, 770, 771, 772, 773, + 774, 775, 776, 777, 0, 0, 0, 0, 0, 0, 784, 785, 786, + 787, 788, 789, 790, 791, 792, 793, 0, 0, 0, 0, 0, 0, + 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 0, 0, 0, + 0, 0, 0, 816, 817, 818, 819, 820, 821, 822, 823, 824, 825, + 0, 0, 0, 0, 0, 0, 832, 833, 834, 835, 836, 837, 838, + 839, 840, 841, 0, 0, 0, 0, 0, 0, 848, 849, 850, 851, + 852, 853, 854, 855, 856, 857, 0, 0, 0, 0, 0, 0, 864, + 865, 866, 867, 868, 869, 870, 871, 872, 873, 0, 0, 0, 0, + 0, 0, 880, 881, 882, 883, 884, 885, 886, 887, 888, 889, 0, + 0, 0, 0, 0, 0, 778, 779, 810, 811, 842, 843, 874, 875, + 846, 847, 0, 0, 0, 0, 0, 0, 794, 795, 826, 827, 858, + 859, 890, 891, 862, 863, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, + 0, 0, 0, 0, 0, 0, 912, 913, 914, 915, 916, 917, 918, + 919, 920, 921, 0, 0, 0, 0, 0, 0, 928, 929, 930, 931, + 932, 933, 934, 935, 936, 937, 0, 0, 0, 0, 0, 0, 944, + 945, 946, 947, 948, 949, 950, 951, 952, 953, 0, 0, 0, 0, + 0, 0, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 0, + 0, 0, 0, 0, 0, 976, 977, 978, 979, 980, 981, 982, 983, + 984, 985, 0, 0, 0, 0, 0, 0, 992, 993, 994, 995, 996, + 997, 998, 999, 1000, 1001, 0, 0, 0, 0, 0, 0, 1008, 1009, + 1010, 1011, 1012, 1013, 1014, 1015, 1016, 1017, 0, 0, 0, 0, 0, + 0, 906, 907, 938, 939, 970, 971, 1002, 1003, 974, 975, 0, 0, + 0, 0, 0, 0, 922, 923, 954, 955, 986, 987, 1018, 1019, 990, + 991, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12, + 13, 268, 269, 524, 525, 780, 781, 46, 47, 0, 0, 0, 0, + 0, 0, 28, 29, 284, 285, 540, 541, 796, 797, 62, 63, 0, + 0, 0, 0, 0, 0, 44, 45, 300, 301, 556, 557, 812, 813, + 302, 303, 0, 0, 0, 0, 0, 0, 60, 61, 316, 317, 572, + 573, 828, 829, 318, 319, 0, 0, 0, 0, 0, 0, 76, 77, + 332, 333, 588, 589, 844, 845, 558, 559, 0, 0, 0, 0, 0, + 0, 92, 93, 348, 349, 604, 605, 860, 861, 574, 575, 0, 0, + 0, 0, 0, 0, 108, 109, 364, 365, 620, 621, 876, 877, 814, + 815, 0, 0, 0, 0, 0, 0, 124, 125, 380, 381, 636, 637, + 892, 893, 830, 831, 0, 0, 0, 0, 0, 0, 14, 15, 270, + 271, 526, 527, 782, 783, 110, 111, 0, 0, 0, 0, 0, 0, + 30, 31, 286, 287, 542, 543, 798, 799, 126, 127, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 140, 141, 396, 397, 652, + 653, 908, 909, 174, 175, 0, 0, 0, 0, 0, 0, 156, 157, + 412, 413, 668, 669, 924, 925, 190, 191, 0, 0, 0, 0, 0, + 0, 172, 173, 428, 429, 684, 685, 940, 941, 430, 431, 0, 0, + 0, 0, 0, 0, 188, 189, 444, 445, 700, 701, 956, 957, 446, + 447, 0, 0, 0, 0, 0, 0, 204, 205, 460, 461, 716, 717, + 972, 973, 686, 687, 0, 0, 0, 0, 0, 0, 220, 221, 476, + 477, 732, 733, 988, 989, 702, 703, 0, 0, 0, 0, 0, 0, + 236, 237, 492, 493, 748, 749, 1004, 1005, 942, 943, 0, 0, 0, + 0, 0, 0, 252, 253, 508, 509, 764, 765, 1020, 1021, 958, 959, + 0, 0, 0, 0, 0, 0, 142, 143, 398, 399, 654, 655, 910, + 911, 238, 239, 0, 0, 0, 0, 0, 0, 158, 159, 414, 415, + 670, 671, 926, 927, 254, 255}; +#endif + +#if defined(DEC_DPD2BCD) && DEC_DPD2BCD==1 && !defined(DECDPD2BCD) +#define DECDPD2BCD + +const uint16_t DPD2BCD[1024]={ 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 128, 129, 2048, 2049, 2176, 2177, 16, 17, 18, 19, 20, + 21, 22, 23, 24, 25, 144, 145, 2064, 2065, 2192, 2193, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 130, 131, 2080, 2081, 2056, + 2057, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 146, 147, + 2096, 2097, 2072, 2073, 64, 65, 66, 67, 68, 69, 70, 71, 72, + 73, 132, 133, 2112, 2113, 136, 137, 80, 81, 82, 83, 84, 85, + 86, 87, 88, 89, 148, 149, 2128, 2129, 152, 153, 96, 97, 98, + 99, 100, 101, 102, 103, 104, 105, 134, 135, 2144, 2145, 2184, 2185, + 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 150, 151, 2160, + 2161, 2200, 2201, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, + 384, 385, 2304, 2305, 2432, 2433, 272, 273, 274, 275, 276, 277, 278, + 279, 280, 281, 400, 401, 2320, 2321, 2448, 2449, 288, 289, 290, 291, + 292, 293, 294, 295, 296, 297, 386, 387, 2336, 2337, 2312, 2313, 304, + 305, 306, 307, 308, 309, 310, 311, 312, 313, 402, 403, 2352, 2353, + 2328, 2329, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 388, + 389, 2368, 2369, 392, 393, 336, 337, 338, 339, 340, 341, 342, 343, + 344, 345, 404, 405, 2384, 2385, 408, 409, 352, 353, 354, 355, 356, + 357, 358, 359, 360, 361, 390, 391, 2400, 2401, 2440, 2441, 368, 369, + 370, 371, 372, 373, 374, 375, 376, 377, 406, 407, 2416, 2417, 2456, + 2457, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 640, 641, + 2050, 2051, 2178, 2179, 528, 529, 530, 531, 532, 533, 534, 535, 536, + 537, 656, 657, 2066, 2067, 2194, 2195, 544, 545, 546, 547, 548, 549, + 550, 551, 552, 553, 642, 643, 2082, 2083, 2088, 2089, 560, 561, 562, + 563, 564, 565, 566, 567, 568, 569, 658, 659, 2098, 2099, 2104, 2105, + 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, 644, 645, 2114, + 2115, 648, 649, 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, + 660, 661, 2130, 2131, 664, 665, 608, 609, 610, 611, 612, 613, 614, + 615, 616, 617, 646, 647, 2146, 2147, 2184, 2185, 624, 625, 626, 627, + 628, 629, 630, 631, 632, 633, 662, 663, 2162, 2163, 2200, 2201, 768, + 769, 770, 771, 772, 773, 774, 775, 776, 777, 896, 897, 2306, 2307, + 2434, 2435, 784, 785, 786, 787, 788, 789, 790, 791, 792, 793, 912, + 913, 2322, 2323, 2450, 2451, 800, 801, 802, 803, 804, 805, 806, 807, + 808, 809, 898, 899, 2338, 2339, 2344, 2345, 816, 817, 818, 819, 820, + 821, 822, 823, 824, 825, 914, 915, 2354, 2355, 2360, 2361, 832, 833, + 834, 835, 836, 837, 838, 839, 840, 841, 900, 901, 2370, 2371, 904, + 905, 848, 849, 850, 851, 852, 853, 854, 855, 856, 857, 916, 917, + 2386, 2387, 920, 921, 864, 865, 866, 867, 868, 869, 870, 871, 872, + 873, 902, 903, 2402, 2403, 2440, 2441, 880, 881, 882, 883, 884, 885, + 886, 887, 888, 889, 918, 919, 2418, 2419, 2456, 2457, 1024, 1025, 1026, + 1027, 1028, 1029, 1030, 1031, 1032, 1033, 1152, 1153, 2052, 2053, 2180, 2181, + 1040, 1041, 1042, 1043, 1044, 1045, 1046, 1047, 1048, 1049, 1168, 1169, 2068, + 2069, 2196, 2197, 1056, 1057, 1058, 1059, 1060, 1061, 1062, 1063, 1064, 1065, + 1154, 1155, 2084, 2085, 2120, 2121, 1072, 1073, 1074, 1075, 1076, 1077, 1078, + 1079, 1080, 1081, 1170, 1171, 2100, 2101, 2136, 2137, 1088, 1089, 1090, 1091, + 1092, 1093, 1094, 1095, 1096, 1097, 1156, 1157, 2116, 2117, 1160, 1161, 1104, + 1105, 1106, 1107, 1108, 1109, 1110, 1111, 1112, 1113, 1172, 1173, 2132, 2133, + 1176, 1177, 1120, 1121, 1122, 1123, 1124, 1125, 1126, 1127, 1128, 1129, 1158, + 1159, 2148, 2149, 2184, 2185, 1136, 1137, 1138, 1139, 1140, 1141, 1142, 1143, + 1144, 1145, 1174, 1175, 2164, 2165, 2200, 2201, 1280, 1281, 1282, 1283, 1284, + 1285, 1286, 1287, 1288, 1289, 1408, 1409, 2308, 2309, 2436, 2437, 1296, 1297, + 1298, 1299, 1300, 1301, 1302, 1303, 1304, 1305, 1424, 1425, 2324, 2325, 2452, + 2453, 1312, 1313, 1314, 1315, 1316, 1317, 1318, 1319, 1320, 1321, 1410, 1411, + 2340, 2341, 2376, 2377, 1328, 1329, 1330, 1331, 1332, 1333, 1334, 1335, 1336, + 1337, 1426, 1427, 2356, 2357, 2392, 2393, 1344, 1345, 1346, 1347, 1348, 1349, + 1350, 1351, 1352, 1353, 1412, 1413, 2372, 2373, 1416, 1417, 1360, 1361, 1362, + 1363, 1364, 1365, 1366, 1367, 1368, 1369, 1428, 1429, 2388, 2389, 1432, 1433, + 1376, 1377, 1378, 1379, 1380, 1381, 1382, 1383, 1384, 1385, 1414, 1415, 2404, + 2405, 2440, 2441, 1392, 1393, 1394, 1395, 1396, 1397, 1398, 1399, 1400, 1401, + 1430, 1431, 2420, 2421, 2456, 2457, 1536, 1537, 1538, 1539, 1540, 1541, 1542, + 1543, 1544, 1545, 1664, 1665, 2054, 2055, 2182, 2183, 1552, 1553, 1554, 1555, + 1556, 1557, 1558, 1559, 1560, 1561, 1680, 1681, 2070, 2071, 2198, 2199, 1568, + 1569, 1570, 1571, 1572, 1573, 1574, 1575, 1576, 1577, 1666, 1667, 2086, 2087, + 2152, 2153, 1584, 1585, 1586, 1587, 1588, 1589, 1590, 1591, 1592, 1593, 1682, + 1683, 2102, 2103, 2168, 2169, 1600, 1601, 1602, 1603, 1604, 1605, 1606, 1607, + 1608, 1609, 1668, 1669, 2118, 2119, 1672, 1673, 1616, 1617, 1618, 1619, 1620, + 1621, 1622, 1623, 1624, 1625, 1684, 1685, 2134, 2135, 1688, 1689, 1632, 1633, + 1634, 1635, 1636, 1637, 1638, 1639, 1640, 1641, 1670, 1671, 2150, 2151, 2184, + 2185, 1648, 1649, 1650, 1651, 1652, 1653, 1654, 1655, 1656, 1657, 1686, 1687, + 2166, 2167, 2200, 2201, 1792, 1793, 1794, 1795, 1796, 1797, 1798, 1799, 1800, + 1801, 1920, 1921, 2310, 2311, 2438, 2439, 1808, 1809, 1810, 1811, 1812, 1813, + 1814, 1815, 1816, 1817, 1936, 1937, 2326, 2327, 2454, 2455, 1824, 1825, 1826, + 1827, 1828, 1829, 1830, 1831, 1832, 1833, 1922, 1923, 2342, 2343, 2408, 2409, + 1840, 1841, 1842, 1843, 1844, 1845, 1846, 1847, 1848, 1849, 1938, 1939, 2358, + 2359, 2424, 2425, 1856, 1857, 1858, 1859, 1860, 1861, 1862, 1863, 1864, 1865, + 1924, 1925, 2374, 2375, 1928, 1929, 1872, 1873, 1874, 1875, 1876, 1877, 1878, + 1879, 1880, 1881, 1940, 1941, 2390, 2391, 1944, 1945, 1888, 1889, 1890, 1891, + 1892, 1893, 1894, 1895, 1896, 1897, 1926, 1927, 2406, 2407, 2440, 2441, 1904, + 1905, 1906, 1907, 1908, 1909, 1910, 1911, 1912, 1913, 1942, 1943, 2422, 2423, + 2456, 2457}; +#endif + +#if defined(DEC_BIN2DPD) && DEC_BIN2DPD==1 && !defined(DECBIN2DPD) +#define DECBIN2DPD + +const uint16_t BIN2DPD[1000]={ 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 32, + 33, 34, 35, 36, 37, 38, 39, 40, 41, 48, 49, 50, 51, + 52, 53, 54, 55, 56, 57, 64, 65, 66, 67, 68, 69, 70, + 71, 72, 73, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 112, 113, 114, + 115, 116, 117, 118, 119, 120, 121, 10, 11, 42, 43, 74, 75, + 106, 107, 78, 79, 26, 27, 58, 59, 90, 91, 122, 123, 94, + 95, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 144, 145, + 146, 147, 148, 149, 150, 151, 152, 153, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 176, 177, 178, 179, 180, 181, 182, 183, + 184, 185, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 208, + 209, 210, 211, 212, 213, 214, 215, 216, 217, 224, 225, 226, 227, + 228, 229, 230, 231, 232, 233, 240, 241, 242, 243, 244, 245, 246, + 247, 248, 249, 138, 139, 170, 171, 202, 203, 234, 235, 206, 207, + 154, 155, 186, 187, 218, 219, 250, 251, 222, 223, 256, 257, 258, + 259, 260, 261, 262, 263, 264, 265, 272, 273, 274, 275, 276, 277, + 278, 279, 280, 281, 288, 289, 290, 291, 292, 293, 294, 295, 296, + 297, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 320, 321, + 322, 323, 324, 325, 326, 327, 328, 329, 336, 337, 338, 339, 340, + 341, 342, 343, 344, 345, 352, 353, 354, 355, 356, 357, 358, 359, + 360, 361, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 266, + 267, 298, 299, 330, 331, 362, 363, 334, 335, 282, 283, 314, 315, + 346, 347, 378, 379, 350, 351, 384, 385, 386, 387, 388, 389, 390, + 391, 392, 393, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, + 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 432, 433, 434, + 435, 436, 437, 438, 439, 440, 441, 448, 449, 450, 451, 452, 453, + 454, 455, 456, 457, 464, 465, 466, 467, 468, 469, 470, 471, 472, + 473, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489, 496, 497, + 498, 499, 500, 501, 502, 503, 504, 505, 394, 395, 426, 427, 458, + 459, 490, 491, 462, 463, 410, 411, 442, 443, 474, 475, 506, 507, + 478, 479, 512, 513, 514, 515, 516, 517, 518, 519, 520, 521, 528, + 529, 530, 531, 532, 533, 534, 535, 536, 537, 544, 545, 546, 547, + 548, 549, 550, 551, 552, 553, 560, 561, 562, 563, 564, 565, 566, + 567, 568, 569, 576, 577, 578, 579, 580, 581, 582, 583, 584, 585, + 592, 593, 594, 595, 596, 597, 598, 599, 600, 601, 608, 609, 610, + 611, 612, 613, 614, 615, 616, 617, 624, 625, 626, 627, 628, 629, + 630, 631, 632, 633, 522, 523, 554, 555, 586, 587, 618, 619, 590, + 591, 538, 539, 570, 571, 602, 603, 634, 635, 606, 607, 640, 641, + 642, 643, 644, 645, 646, 647, 648, 649, 656, 657, 658, 659, 660, + 661, 662, 663, 664, 665, 672, 673, 674, 675, 676, 677, 678, 679, + 680, 681, 688, 689, 690, 691, 692, 693, 694, 695, 696, 697, 704, + 705, 706, 707, 708, 709, 710, 711, 712, 713, 720, 721, 722, 723, + 724, 725, 726, 727, 728, 729, 736, 737, 738, 739, 740, 741, 742, + 743, 744, 745, 752, 753, 754, 755, 756, 757, 758, 759, 760, 761, + 650, 651, 682, 683, 714, 715, 746, 747, 718, 719, 666, 667, 698, + 699, 730, 731, 762, 763, 734, 735, 768, 769, 770, 771, 772, 773, + 774, 775, 776, 777, 784, 785, 786, 787, 788, 789, 790, 791, 792, + 793, 800, 801, 802, 803, 804, 805, 806, 807, 808, 809, 816, 817, + 818, 819, 820, 821, 822, 823, 824, 825, 832, 833, 834, 835, 836, + 837, 838, 839, 840, 841, 848, 849, 850, 851, 852, 853, 854, 855, + 856, 857, 864, 865, 866, 867, 868, 869, 870, 871, 872, 873, 880, + 881, 882, 883, 884, 885, 886, 887, 888, 889, 778, 779, 810, 811, + 842, 843, 874, 875, 846, 847, 794, 795, 826, 827, 858, 859, 890, + 891, 862, 863, 896, 897, 898, 899, 900, 901, 902, 903, 904, 905, + 912, 913, 914, 915, 916, 917, 918, 919, 920, 921, 928, 929, 930, + 931, 932, 933, 934, 935, 936, 937, 944, 945, 946, 947, 948, 949, + 950, 951, 952, 953, 960, 961, 962, 963, 964, 965, 966, 967, 968, + 969, 976, 977, 978, 979, 980, 981, 982, 983, 984, 985, 992, 993, + 994, 995, 996, 997, 998, 999, 1000, 1001, 1008, 1009, 1010, 1011, 1012, + 1013, 1014, 1015, 1016, 1017, 906, 907, 938, 939, 970, 971, 1002, 1003, + 974, 975, 922, 923, 954, 955, 986, 987, 1018, 1019, 990, 991, 12, + 13, 268, 269, 524, 525, 780, 781, 46, 47, 28, 29, 284, 285, + 540, 541, 796, 797, 62, 63, 44, 45, 300, 301, 556, 557, 812, + 813, 302, 303, 60, 61, 316, 317, 572, 573, 828, 829, 318, 319, + 76, 77, 332, 333, 588, 589, 844, 845, 558, 559, 92, 93, 348, + 349, 604, 605, 860, 861, 574, 575, 108, 109, 364, 365, 620, 621, + 876, 877, 814, 815, 124, 125, 380, 381, 636, 637, 892, 893, 830, + 831, 14, 15, 270, 271, 526, 527, 782, 783, 110, 111, 30, 31, + 286, 287, 542, 543, 798, 799, 126, 127, 140, 141, 396, 397, 652, + 653, 908, 909, 174, 175, 156, 157, 412, 413, 668, 669, 924, 925, + 190, 191, 172, 173, 428, 429, 684, 685, 940, 941, 430, 431, 188, + 189, 444, 445, 700, 701, 956, 957, 446, 447, 204, 205, 460, 461, + 716, 717, 972, 973, 686, 687, 220, 221, 476, 477, 732, 733, 988, + 989, 702, 703, 236, 237, 492, 493, 748, 749, 1004, 1005, 942, 943, + 252, 253, 508, 509, 764, 765, 1020, 1021, 958, 959, 142, 143, 398, + 399, 654, 655, 910, 911, 238, 239, 158, 159, 414, 415, 670, 671, + 926, 927, 254, 255}; +#endif + +#if defined(DEC_DPD2BIN) && DEC_DPD2BIN==1 && !defined(DECDPD2BIN) +#define DECDPD2BIN + +const uint16_t DPD2BIN[1024]={ 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 80, 81, 800, 801, 880, 881, 10, 11, 12, 13, 14, + 15, 16, 17, 18, 19, 90, 91, 810, 811, 890, 891, 20, 21, + 22, 23, 24, 25, 26, 27, 28, 29, 82, 83, 820, 821, 808, + 809, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 92, 93, + 830, 831, 818, 819, 40, 41, 42, 43, 44, 45, 46, 47, 48, + 49, 84, 85, 840, 841, 88, 89, 50, 51, 52, 53, 54, 55, + 56, 57, 58, 59, 94, 95, 850, 851, 98, 99, 60, 61, 62, + 63, 64, 65, 66, 67, 68, 69, 86, 87, 860, 861, 888, 889, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 96, 97, 870, + 871, 898, 899, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, + 180, 181, 900, 901, 980, 981, 110, 111, 112, 113, 114, 115, 116, + 117, 118, 119, 190, 191, 910, 911, 990, 991, 120, 121, 122, 123, + 124, 125, 126, 127, 128, 129, 182, 183, 920, 921, 908, 909, 130, + 131, 132, 133, 134, 135, 136, 137, 138, 139, 192, 193, 930, 931, + 918, 919, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 184, + 185, 940, 941, 188, 189, 150, 151, 152, 153, 154, 155, 156, 157, + 158, 159, 194, 195, 950, 951, 198, 199, 160, 161, 162, 163, 164, + 165, 166, 167, 168, 169, 186, 187, 960, 961, 988, 989, 170, 171, + 172, 173, 174, 175, 176, 177, 178, 179, 196, 197, 970, 971, 998, + 999, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 280, 281, + 802, 803, 882, 883, 210, 211, 212, 213, 214, 215, 216, 217, 218, + 219, 290, 291, 812, 813, 892, 893, 220, 221, 222, 223, 224, 225, + 226, 227, 228, 229, 282, 283, 822, 823, 828, 829, 230, 231, 232, + 233, 234, 235, 236, 237, 238, 239, 292, 293, 832, 833, 838, 839, + 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 284, 285, 842, + 843, 288, 289, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, + 294, 295, 852, 853, 298, 299, 260, 261, 262, 263, 264, 265, 266, + 267, 268, 269, 286, 287, 862, 863, 888, 889, 270, 271, 272, 273, + 274, 275, 276, 277, 278, 279, 296, 297, 872, 873, 898, 899, 300, + 301, 302, 303, 304, 305, 306, 307, 308, 309, 380, 381, 902, 903, + 982, 983, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 390, + 391, 912, 913, 992, 993, 320, 321, 322, 323, 324, 325, 326, 327, + 328, 329, 382, 383, 922, 923, 928, 929, 330, 331, 332, 333, 334, + 335, 336, 337, 338, 339, 392, 393, 932, 933, 938, 939, 340, 341, + 342, 343, 344, 345, 346, 347, 348, 349, 384, 385, 942, 943, 388, + 389, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 394, 395, + 952, 953, 398, 399, 360, 361, 362, 363, 364, 365, 366, 367, 368, + 369, 386, 387, 962, 963, 988, 989, 370, 371, 372, 373, 374, 375, + 376, 377, 378, 379, 396, 397, 972, 973, 998, 999, 400, 401, 402, + 403, 404, 405, 406, 407, 408, 409, 480, 481, 804, 805, 884, 885, + 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 490, 491, 814, + 815, 894, 895, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, + 482, 483, 824, 825, 848, 849, 430, 431, 432, 433, 434, 435, 436, + 437, 438, 439, 492, 493, 834, 835, 858, 859, 440, 441, 442, 443, + 444, 445, 446, 447, 448, 449, 484, 485, 844, 845, 488, 489, 450, + 451, 452, 453, 454, 455, 456, 457, 458, 459, 494, 495, 854, 855, + 498, 499, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 486, + 487, 864, 865, 888, 889, 470, 471, 472, 473, 474, 475, 476, 477, + 478, 479, 496, 497, 874, 875, 898, 899, 500, 501, 502, 503, 504, + 505, 506, 507, 508, 509, 580, 581, 904, 905, 984, 985, 510, 511, + 512, 513, 514, 515, 516, 517, 518, 519, 590, 591, 914, 915, 994, + 995, 520, 521, 522, 523, 524, 525, 526, 527, 528, 529, 582, 583, + 924, 925, 948, 949, 530, 531, 532, 533, 534, 535, 536, 537, 538, + 539, 592, 593, 934, 935, 958, 959, 540, 541, 542, 543, 544, 545, + 546, 547, 548, 549, 584, 585, 944, 945, 588, 589, 550, 551, 552, + 553, 554, 555, 556, 557, 558, 559, 594, 595, 954, 955, 598, 599, + 560, 561, 562, 563, 564, 565, 566, 567, 568, 569, 586, 587, 964, + 965, 988, 989, 570, 571, 572, 573, 574, 575, 576, 577, 578, 579, + 596, 597, 974, 975, 998, 999, 600, 601, 602, 603, 604, 605, 606, + 607, 608, 609, 680, 681, 806, 807, 886, 887, 610, 611, 612, 613, + 614, 615, 616, 617, 618, 619, 690, 691, 816, 817, 896, 897, 620, + 621, 622, 623, 624, 625, 626, 627, 628, 629, 682, 683, 826, 827, + 868, 869, 630, 631, 632, 633, 634, 635, 636, 637, 638, 639, 692, + 693, 836, 837, 878, 879, 640, 641, 642, 643, 644, 645, 646, 647, + 648, 649, 684, 685, 846, 847, 688, 689, 650, 651, 652, 653, 654, + 655, 656, 657, 658, 659, 694, 695, 856, 857, 698, 699, 660, 661, + 662, 663, 664, 665, 666, 667, 668, 669, 686, 687, 866, 867, 888, + 889, 670, 671, 672, 673, 674, 675, 676, 677, 678, 679, 696, 697, + 876, 877, 898, 899, 700, 701, 702, 703, 704, 705, 706, 707, 708, + 709, 780, 781, 906, 907, 986, 987, 710, 711, 712, 713, 714, 715, + 716, 717, 718, 719, 790, 791, 916, 917, 996, 997, 720, 721, 722, + 723, 724, 725, 726, 727, 728, 729, 782, 783, 926, 927, 968, 969, + 730, 731, 732, 733, 734, 735, 736, 737, 738, 739, 792, 793, 936, + 937, 978, 979, 740, 741, 742, 743, 744, 745, 746, 747, 748, 749, + 784, 785, 946, 947, 788, 789, 750, 751, 752, 753, 754, 755, 756, + 757, 758, 759, 794, 795, 956, 957, 798, 799, 760, 761, 762, 763, + 764, 765, 766, 767, 768, 769, 786, 787, 966, 967, 988, 989, 770, + 771, 772, 773, 774, 775, 776, 777, 778, 779, 796, 797, 976, 977, + 998, 999}; +#endif + +#if defined(DEC_DPD2BINK) && DEC_DPD2BINK==1 && !defined(DECDPD2BINK) +#define DECDPD2BINK + +const uint32_t DPD2BINK[1024]={ 0, 1000, 2000, 3000, 4000, 5000, + 6000, 7000, 8000, 9000, 80000, 81000, 800000, 801000, 880000, 881000, + 10000, 11000, 12000, 13000, 14000, 15000, 16000, 17000, 18000, 19000, + 90000, 91000, 810000, 811000, 890000, 891000, 20000, 21000, 22000, 23000, + 24000, 25000, 26000, 27000, 28000, 29000, 82000, 83000, 820000, 821000, + 808000, 809000, 30000, 31000, 32000, 33000, 34000, 35000, 36000, 37000, + 38000, 39000, 92000, 93000, 830000, 831000, 818000, 819000, 40000, 41000, + 42000, 43000, 44000, 45000, 46000, 47000, 48000, 49000, 84000, 85000, + 840000, 841000, 88000, 89000, 50000, 51000, 52000, 53000, 54000, 55000, + 56000, 57000, 58000, 59000, 94000, 95000, 850000, 851000, 98000, 99000, + 60000, 61000, 62000, 63000, 64000, 65000, 66000, 67000, 68000, 69000, + 86000, 87000, 860000, 861000, 888000, 889000, 70000, 71000, 72000, 73000, + 74000, 75000, 76000, 77000, 78000, 79000, 96000, 97000, 870000, 871000, + 898000, 899000, 100000, 101000, 102000, 103000, 104000, 105000, 106000, 107000, + 108000, 109000, 180000, 181000, 900000, 901000, 980000, 981000, 110000, 111000, + 112000, 113000, 114000, 115000, 116000, 117000, 118000, 119000, 190000, 191000, + 910000, 911000, 990000, 991000, 120000, 121000, 122000, 123000, 124000, 125000, + 126000, 127000, 128000, 129000, 182000, 183000, 920000, 921000, 908000, 909000, + 130000, 131000, 132000, 133000, 134000, 135000, 136000, 137000, 138000, 139000, + 192000, 193000, 930000, 931000, 918000, 919000, 140000, 141000, 142000, 143000, + 144000, 145000, 146000, 147000, 148000, 149000, 184000, 185000, 940000, 941000, + 188000, 189000, 150000, 151000, 152000, 153000, 154000, 155000, 156000, 157000, + 158000, 159000, 194000, 195000, 950000, 951000, 198000, 199000, 160000, 161000, + 162000, 163000, 164000, 165000, 166000, 167000, 168000, 169000, 186000, 187000, + 960000, 961000, 988000, 989000, 170000, 171000, 172000, 173000, 174000, 175000, + 176000, 177000, 178000, 179000, 196000, 197000, 970000, 971000, 998000, 999000, + 200000, 201000, 202000, 203000, 204000, 205000, 206000, 207000, 208000, 209000, + 280000, 281000, 802000, 803000, 882000, 883000, 210000, 211000, 212000, 213000, + 214000, 215000, 216000, 217000, 218000, 219000, 290000, 291000, 812000, 813000, + 892000, 893000, 220000, 221000, 222000, 223000, 224000, 225000, 226000, 227000, + 228000, 229000, 282000, 283000, 822000, 823000, 828000, 829000, 230000, 231000, + 232000, 233000, 234000, 235000, 236000, 237000, 238000, 239000, 292000, 293000, + 832000, 833000, 838000, 839000, 240000, 241000, 242000, 243000, 244000, 245000, + 246000, 247000, 248000, 249000, 284000, 285000, 842000, 843000, 288000, 289000, + 250000, 251000, 252000, 253000, 254000, 255000, 256000, 257000, 258000, 259000, + 294000, 295000, 852000, 853000, 298000, 299000, 260000, 261000, 262000, 263000, + 264000, 265000, 266000, 267000, 268000, 269000, 286000, 287000, 862000, 863000, + 888000, 889000, 270000, 271000, 272000, 273000, 274000, 275000, 276000, 277000, + 278000, 279000, 296000, 297000, 872000, 873000, 898000, 899000, 300000, 301000, + 302000, 303000, 304000, 305000, 306000, 307000, 308000, 309000, 380000, 381000, + 902000, 903000, 982000, 983000, 310000, 311000, 312000, 313000, 314000, 315000, + 316000, 317000, 318000, 319000, 390000, 391000, 912000, 913000, 992000, 993000, + 320000, 321000, 322000, 323000, 324000, 325000, 326000, 327000, 328000, 329000, + 382000, 383000, 922000, 923000, 928000, 929000, 330000, 331000, 332000, 333000, + 334000, 335000, 336000, 337000, 338000, 339000, 392000, 393000, 932000, 933000, + 938000, 939000, 340000, 341000, 342000, 343000, 344000, 345000, 346000, 347000, + 348000, 349000, 384000, 385000, 942000, 943000, 388000, 389000, 350000, 351000, + 352000, 353000, 354000, 355000, 356000, 357000, 358000, 359000, 394000, 395000, + 952000, 953000, 398000, 399000, 360000, 361000, 362000, 363000, 364000, 365000, + 366000, 367000, 368000, 369000, 386000, 387000, 962000, 963000, 988000, 989000, + 370000, 371000, 372000, 373000, 374000, 375000, 376000, 377000, 378000, 379000, + 396000, 397000, 972000, 973000, 998000, 999000, 400000, 401000, 402000, 403000, + 404000, 405000, 406000, 407000, 408000, 409000, 480000, 481000, 804000, 805000, + 884000, 885000, 410000, 411000, 412000, 413000, 414000, 415000, 416000, 417000, + 418000, 419000, 490000, 491000, 814000, 815000, 894000, 895000, 420000, 421000, + 422000, 423000, 424000, 425000, 426000, 427000, 428000, 429000, 482000, 483000, + 824000, 825000, 848000, 849000, 430000, 431000, 432000, 433000, 434000, 435000, + 436000, 437000, 438000, 439000, 492000, 493000, 834000, 835000, 858000, 859000, + 440000, 441000, 442000, 443000, 444000, 445000, 446000, 447000, 448000, 449000, + 484000, 485000, 844000, 845000, 488000, 489000, 450000, 451000, 452000, 453000, + 454000, 455000, 456000, 457000, 458000, 459000, 494000, 495000, 854000, 855000, + 498000, 499000, 460000, 461000, 462000, 463000, 464000, 465000, 466000, 467000, + 468000, 469000, 486000, 487000, 864000, 865000, 888000, 889000, 470000, 471000, + 472000, 473000, 474000, 475000, 476000, 477000, 478000, 479000, 496000, 497000, + 874000, 875000, 898000, 899000, 500000, 501000, 502000, 503000, 504000, 505000, + 506000, 507000, 508000, 509000, 580000, 581000, 904000, 905000, 984000, 985000, + 510000, 511000, 512000, 513000, 514000, 515000, 516000, 517000, 518000, 519000, + 590000, 591000, 914000, 915000, 994000, 995000, 520000, 521000, 522000, 523000, + 524000, 525000, 526000, 527000, 528000, 529000, 582000, 583000, 924000, 925000, + 948000, 949000, 530000, 531000, 532000, 533000, 534000, 535000, 536000, 537000, + 538000, 539000, 592000, 593000, 934000, 935000, 958000, 959000, 540000, 541000, + 542000, 543000, 544000, 545000, 546000, 547000, 548000, 549000, 584000, 585000, + 944000, 945000, 588000, 589000, 550000, 551000, 552000, 553000, 554000, 555000, + 556000, 557000, 558000, 559000, 594000, 595000, 954000, 955000, 598000, 599000, + 560000, 561000, 562000, 563000, 564000, 565000, 566000, 567000, 568000, 569000, + 586000, 587000, 964000, 965000, 988000, 989000, 570000, 571000, 572000, 573000, + 574000, 575000, 576000, 577000, 578000, 579000, 596000, 597000, 974000, 975000, + 998000, 999000, 600000, 601000, 602000, 603000, 604000, 605000, 606000, 607000, + 608000, 609000, 680000, 681000, 806000, 807000, 886000, 887000, 610000, 611000, + 612000, 613000, 614000, 615000, 616000, 617000, 618000, 619000, 690000, 691000, + 816000, 817000, 896000, 897000, 620000, 621000, 622000, 623000, 624000, 625000, + 626000, 627000, 628000, 629000, 682000, 683000, 826000, 827000, 868000, 869000, + 630000, 631000, 632000, 633000, 634000, 635000, 636000, 637000, 638000, 639000, + 692000, 693000, 836000, 837000, 878000, 879000, 640000, 641000, 642000, 643000, + 644000, 645000, 646000, 647000, 648000, 649000, 684000, 685000, 846000, 847000, + 688000, 689000, 650000, 651000, 652000, 653000, 654000, 655000, 656000, 657000, + 658000, 659000, 694000, 695000, 856000, 857000, 698000, 699000, 660000, 661000, + 662000, 663000, 664000, 665000, 666000, 667000, 668000, 669000, 686000, 687000, + 866000, 867000, 888000, 889000, 670000, 671000, 672000, 673000, 674000, 675000, + 676000, 677000, 678000, 679000, 696000, 697000, 876000, 877000, 898000, 899000, + 700000, 701000, 702000, 703000, 704000, 705000, 706000, 707000, 708000, 709000, + 780000, 781000, 906000, 907000, 986000, 987000, 710000, 711000, 712000, 713000, + 714000, 715000, 716000, 717000, 718000, 719000, 790000, 791000, 916000, 917000, + 996000, 997000, 720000, 721000, 722000, 723000, 724000, 725000, 726000, 727000, + 728000, 729000, 782000, 783000, 926000, 927000, 968000, 969000, 730000, 731000, + 732000, 733000, 734000, 735000, 736000, 737000, 738000, 739000, 792000, 793000, + 936000, 937000, 978000, 979000, 740000, 741000, 742000, 743000, 744000, 745000, + 746000, 747000, 748000, 749000, 784000, 785000, 946000, 947000, 788000, 789000, + 750000, 751000, 752000, 753000, 754000, 755000, 756000, 757000, 758000, 759000, + 794000, 795000, 956000, 957000, 798000, 799000, 760000, 761000, 762000, 763000, + 764000, 765000, 766000, 767000, 768000, 769000, 786000, 787000, 966000, 967000, + 988000, 989000, 770000, 771000, 772000, 773000, 774000, 775000, 776000, 777000, + 778000, 779000, 796000, 797000, 976000, 977000, 998000, 999000}; +#endif + +#if defined(DEC_DPD2BINM) && DEC_DPD2BINM==1 && !defined(DECDPD2BINM) +#define DECDPD2BINM + +const uint32_t DPD2BINM[1024]={0, 1000000, 2000000, 3000000, 4000000, + 5000000, 6000000, 7000000, 8000000, 9000000, 80000000, 81000000, + 800000000, 801000000, 880000000, 881000000, 10000000, 11000000, 12000000, + 13000000, 14000000, 15000000, 16000000, 17000000, 18000000, 19000000, + 90000000, 91000000, 810000000, 811000000, 890000000, 891000000, 20000000, + 21000000, 22000000, 23000000, 24000000, 25000000, 26000000, 27000000, + 28000000, 29000000, 82000000, 83000000, 820000000, 821000000, 808000000, + 809000000, 30000000, 31000000, 32000000, 33000000, 34000000, 35000000, + 36000000, 37000000, 38000000, 39000000, 92000000, 93000000, 830000000, + 831000000, 818000000, 819000000, 40000000, 41000000, 42000000, 43000000, + 44000000, 45000000, 46000000, 47000000, 48000000, 49000000, 84000000, + 85000000, 840000000, 841000000, 88000000, 89000000, 50000000, 51000000, + 52000000, 53000000, 54000000, 55000000, 56000000, 57000000, 58000000, + 59000000, 94000000, 95000000, 850000000, 851000000, 98000000, 99000000, + 60000000, 61000000, 62000000, 63000000, 64000000, 65000000, 66000000, + 67000000, 68000000, 69000000, 86000000, 87000000, 860000000, 861000000, + 888000000, 889000000, 70000000, 71000000, 72000000, 73000000, 74000000, + 75000000, 76000000, 77000000, 78000000, 79000000, 96000000, 97000000, + 870000000, 871000000, 898000000, 899000000, 100000000, 101000000, 102000000, + 103000000, 104000000, 105000000, 106000000, 107000000, 108000000, 109000000, + 180000000, 181000000, 900000000, 901000000, 980000000, 981000000, 110000000, + 111000000, 112000000, 113000000, 114000000, 115000000, 116000000, 117000000, + 118000000, 119000000, 190000000, 191000000, 910000000, 911000000, 990000000, + 991000000, 120000000, 121000000, 122000000, 123000000, 124000000, 125000000, + 126000000, 127000000, 128000000, 129000000, 182000000, 183000000, 920000000, + 921000000, 908000000, 909000000, 130000000, 131000000, 132000000, 133000000, + 134000000, 135000000, 136000000, 137000000, 138000000, 139000000, 192000000, + 193000000, 930000000, 931000000, 918000000, 919000000, 140000000, 141000000, + 142000000, 143000000, 144000000, 145000000, 146000000, 147000000, 148000000, + 149000000, 184000000, 185000000, 940000000, 941000000, 188000000, 189000000, + 150000000, 151000000, 152000000, 153000000, 154000000, 155000000, 156000000, + 157000000, 158000000, 159000000, 194000000, 195000000, 950000000, 951000000, + 198000000, 199000000, 160000000, 161000000, 162000000, 163000000, 164000000, + 165000000, 166000000, 167000000, 168000000, 169000000, 186000000, 187000000, + 960000000, 961000000, 988000000, 989000000, 170000000, 171000000, 172000000, + 173000000, 174000000, 175000000, 176000000, 177000000, 178000000, 179000000, + 196000000, 197000000, 970000000, 971000000, 998000000, 999000000, 200000000, + 201000000, 202000000, 203000000, 204000000, 205000000, 206000000, 207000000, + 208000000, 209000000, 280000000, 281000000, 802000000, 803000000, 882000000, + 883000000, 210000000, 211000000, 212000000, 213000000, 214000000, 215000000, + 216000000, 217000000, 218000000, 219000000, 290000000, 291000000, 812000000, + 813000000, 892000000, 893000000, 220000000, 221000000, 222000000, 223000000, + 224000000, 225000000, 226000000, 227000000, 228000000, 229000000, 282000000, + 283000000, 822000000, 823000000, 828000000, 829000000, 230000000, 231000000, + 232000000, 233000000, 234000000, 235000000, 236000000, 237000000, 238000000, + 239000000, 292000000, 293000000, 832000000, 833000000, 838000000, 839000000, + 240000000, 241000000, 242000000, 243000000, 244000000, 245000000, 246000000, + 247000000, 248000000, 249000000, 284000000, 285000000, 842000000, 843000000, + 288000000, 289000000, 250000000, 251000000, 252000000, 253000000, 254000000, + 255000000, 256000000, 257000000, 258000000, 259000000, 294000000, 295000000, + 852000000, 853000000, 298000000, 299000000, 260000000, 261000000, 262000000, + 263000000, 264000000, 265000000, 266000000, 267000000, 268000000, 269000000, + 286000000, 287000000, 862000000, 863000000, 888000000, 889000000, 270000000, + 271000000, 272000000, 273000000, 274000000, 275000000, 276000000, 277000000, + 278000000, 279000000, 296000000, 297000000, 872000000, 873000000, 898000000, + 899000000, 300000000, 301000000, 302000000, 303000000, 304000000, 305000000, + 306000000, 307000000, 308000000, 309000000, 380000000, 381000000, 902000000, + 903000000, 982000000, 983000000, 310000000, 311000000, 312000000, 313000000, + 314000000, 315000000, 316000000, 317000000, 318000000, 319000000, 390000000, + 391000000, 912000000, 913000000, 992000000, 993000000, 320000000, 321000000, + 322000000, 323000000, 324000000, 325000000, 326000000, 327000000, 328000000, + 329000000, 382000000, 383000000, 922000000, 923000000, 928000000, 929000000, + 330000000, 331000000, 332000000, 333000000, 334000000, 335000000, 336000000, + 337000000, 338000000, 339000000, 392000000, 393000000, 932000000, 933000000, + 938000000, 939000000, 340000000, 341000000, 342000000, 343000000, 344000000, + 345000000, 346000000, 347000000, 348000000, 349000000, 384000000, 385000000, + 942000000, 943000000, 388000000, 389000000, 350000000, 351000000, 352000000, + 353000000, 354000000, 355000000, 356000000, 357000000, 358000000, 359000000, + 394000000, 395000000, 952000000, 953000000, 398000000, 399000000, 360000000, + 361000000, 362000000, 363000000, 364000000, 365000000, 366000000, 367000000, + 368000000, 369000000, 386000000, 387000000, 962000000, 963000000, 988000000, + 989000000, 370000000, 371000000, 372000000, 373000000, 374000000, 375000000, + 376000000, 377000000, 378000000, 379000000, 396000000, 397000000, 972000000, + 973000000, 998000000, 999000000, 400000000, 401000000, 402000000, 403000000, + 404000000, 405000000, 406000000, 407000000, 408000000, 409000000, 480000000, + 481000000, 804000000, 805000000, 884000000, 885000000, 410000000, 411000000, + 412000000, 413000000, 414000000, 415000000, 416000000, 417000000, 418000000, + 419000000, 490000000, 491000000, 814000000, 815000000, 894000000, 895000000, + 420000000, 421000000, 422000000, 423000000, 424000000, 425000000, 426000000, + 427000000, 428000000, 429000000, 482000000, 483000000, 824000000, 825000000, + 848000000, 849000000, 430000000, 431000000, 432000000, 433000000, 434000000, + 435000000, 436000000, 437000000, 438000000, 439000000, 492000000, 493000000, + 834000000, 835000000, 858000000, 859000000, 440000000, 441000000, 442000000, + 443000000, 444000000, 445000000, 446000000, 447000000, 448000000, 449000000, + 484000000, 485000000, 844000000, 845000000, 488000000, 489000000, 450000000, + 451000000, 452000000, 453000000, 454000000, 455000000, 456000000, 457000000, + 458000000, 459000000, 494000000, 495000000, 854000000, 855000000, 498000000, + 499000000, 460000000, 461000000, 462000000, 463000000, 464000000, 465000000, + 466000000, 467000000, 468000000, 469000000, 486000000, 487000000, 864000000, + 865000000, 888000000, 889000000, 470000000, 471000000, 472000000, 473000000, + 474000000, 475000000, 476000000, 477000000, 478000000, 479000000, 496000000, + 497000000, 874000000, 875000000, 898000000, 899000000, 500000000, 501000000, + 502000000, 503000000, 504000000, 505000000, 506000000, 507000000, 508000000, + 509000000, 580000000, 581000000, 904000000, 905000000, 984000000, 985000000, + 510000000, 511000000, 512000000, 513000000, 514000000, 515000000, 516000000, + 517000000, 518000000, 519000000, 590000000, 591000000, 914000000, 915000000, + 994000000, 995000000, 520000000, 521000000, 522000000, 523000000, 524000000, + 525000000, 526000000, 527000000, 528000000, 529000000, 582000000, 583000000, + 924000000, 925000000, 948000000, 949000000, 530000000, 531000000, 532000000, + 533000000, 534000000, 535000000, 536000000, 537000000, 538000000, 539000000, + 592000000, 593000000, 934000000, 935000000, 958000000, 959000000, 540000000, + 541000000, 542000000, 543000000, 544000000, 545000000, 546000000, 547000000, + 548000000, 549000000, 584000000, 585000000, 944000000, 945000000, 588000000, + 589000000, 550000000, 551000000, 552000000, 553000000, 554000000, 555000000, + 556000000, 557000000, 558000000, 559000000, 594000000, 595000000, 954000000, + 955000000, 598000000, 599000000, 560000000, 561000000, 562000000, 563000000, + 564000000, 565000000, 566000000, 567000000, 568000000, 569000000, 586000000, + 587000000, 964000000, 965000000, 988000000, 989000000, 570000000, 571000000, + 572000000, 573000000, 574000000, 575000000, 576000000, 577000000, 578000000, + 579000000, 596000000, 597000000, 974000000, 975000000, 998000000, 999000000, + 600000000, 601000000, 602000000, 603000000, 604000000, 605000000, 606000000, + 607000000, 608000000, 609000000, 680000000, 681000000, 806000000, 807000000, + 886000000, 887000000, 610000000, 611000000, 612000000, 613000000, 614000000, + 615000000, 616000000, 617000000, 618000000, 619000000, 690000000, 691000000, + 816000000, 817000000, 896000000, 897000000, 620000000, 621000000, 622000000, + 623000000, 624000000, 625000000, 626000000, 627000000, 628000000, 629000000, + 682000000, 683000000, 826000000, 827000000, 868000000, 869000000, 630000000, + 631000000, 632000000, 633000000, 634000000, 635000000, 636000000, 637000000, + 638000000, 639000000, 692000000, 693000000, 836000000, 837000000, 878000000, + 879000000, 640000000, 641000000, 642000000, 643000000, 644000000, 645000000, + 646000000, 647000000, 648000000, 649000000, 684000000, 685000000, 846000000, + 847000000, 688000000, 689000000, 650000000, 651000000, 652000000, 653000000, + 654000000, 655000000, 656000000, 657000000, 658000000, 659000000, 694000000, + 695000000, 856000000, 857000000, 698000000, 699000000, 660000000, 661000000, + 662000000, 663000000, 664000000, 665000000, 666000000, 667000000, 668000000, + 669000000, 686000000, 687000000, 866000000, 867000000, 888000000, 889000000, + 670000000, 671000000, 672000000, 673000000, 674000000, 675000000, 676000000, + 677000000, 678000000, 679000000, 696000000, 697000000, 876000000, 877000000, + 898000000, 899000000, 700000000, 701000000, 702000000, 703000000, 704000000, + 705000000, 706000000, 707000000, 708000000, 709000000, 780000000, 781000000, + 906000000, 907000000, 986000000, 987000000, 710000000, 711000000, 712000000, + 713000000, 714000000, 715000000, 716000000, 717000000, 718000000, 719000000, + 790000000, 791000000, 916000000, 917000000, 996000000, 997000000, 720000000, + 721000000, 722000000, 723000000, 724000000, 725000000, 726000000, 727000000, + 728000000, 729000000, 782000000, 783000000, 926000000, 927000000, 968000000, + 969000000, 730000000, 731000000, 732000000, 733000000, 734000000, 735000000, + 736000000, 737000000, 738000000, 739000000, 792000000, 793000000, 936000000, + 937000000, 978000000, 979000000, 740000000, 741000000, 742000000, 743000000, + 744000000, 745000000, 746000000, 747000000, 748000000, 749000000, 784000000, + 785000000, 946000000, 947000000, 788000000, 789000000, 750000000, 751000000, + 752000000, 753000000, 754000000, 755000000, 756000000, 757000000, 758000000, + 759000000, 794000000, 795000000, 956000000, 957000000, 798000000, 799000000, + 760000000, 761000000, 762000000, 763000000, 764000000, 765000000, 766000000, + 767000000, 768000000, 769000000, 786000000, 787000000, 966000000, 967000000, + 988000000, 989000000, 770000000, 771000000, 772000000, 773000000, 774000000, + 775000000, 776000000, 777000000, 778000000, 779000000, 796000000, 797000000, + 976000000, 977000000, 998000000, 999000000}; +#endif + +#if defined(DEC_BIN2CHAR) && DEC_BIN2CHAR==1 && !defined(DECBIN2CHAR) +#define DECBIN2CHAR + +const uint8_t BIN2CHAR[4001]={ + '\0','0','0','0', '\1','0','0','1', '\1','0','0','2', '\1','0','0','3', '\1','0','0','4', + '\1','0','0','5', '\1','0','0','6', '\1','0','0','7', '\1','0','0','8', '\1','0','0','9', + '\2','0','1','0', '\2','0','1','1', '\2','0','1','2', '\2','0','1','3', '\2','0','1','4', + '\2','0','1','5', '\2','0','1','6', '\2','0','1','7', '\2','0','1','8', '\2','0','1','9', + '\2','0','2','0', '\2','0','2','1', '\2','0','2','2', '\2','0','2','3', '\2','0','2','4', + '\2','0','2','5', '\2','0','2','6', '\2','0','2','7', '\2','0','2','8', '\2','0','2','9', + '\2','0','3','0', '\2','0','3','1', '\2','0','3','2', '\2','0','3','3', '\2','0','3','4', + '\2','0','3','5', '\2','0','3','6', '\2','0','3','7', '\2','0','3','8', '\2','0','3','9', + '\2','0','4','0', '\2','0','4','1', '\2','0','4','2', '\2','0','4','3', '\2','0','4','4', + '\2','0','4','5', '\2','0','4','6', '\2','0','4','7', '\2','0','4','8', '\2','0','4','9', + '\2','0','5','0', '\2','0','5','1', '\2','0','5','2', '\2','0','5','3', '\2','0','5','4', + '\2','0','5','5', '\2','0','5','6', '\2','0','5','7', '\2','0','5','8', '\2','0','5','9', + '\2','0','6','0', '\2','0','6','1', '\2','0','6','2', '\2','0','6','3', '\2','0','6','4', + '\2','0','6','5', '\2','0','6','6', '\2','0','6','7', '\2','0','6','8', '\2','0','6','9', + '\2','0','7','0', '\2','0','7','1', '\2','0','7','2', '\2','0','7','3', '\2','0','7','4', + '\2','0','7','5', '\2','0','7','6', '\2','0','7','7', '\2','0','7','8', '\2','0','7','9', + '\2','0','8','0', '\2','0','8','1', '\2','0','8','2', '\2','0','8','3', '\2','0','8','4', + '\2','0','8','5', '\2','0','8','6', '\2','0','8','7', '\2','0','8','8', '\2','0','8','9', + '\2','0','9','0', '\2','0','9','1', '\2','0','9','2', '\2','0','9','3', '\2','0','9','4', + '\2','0','9','5', '\2','0','9','6', '\2','0','9','7', '\2','0','9','8', '\2','0','9','9', + '\3','1','0','0', '\3','1','0','1', '\3','1','0','2', '\3','1','0','3', '\3','1','0','4', + '\3','1','0','5', '\3','1','0','6', '\3','1','0','7', '\3','1','0','8', '\3','1','0','9', + '\3','1','1','0', '\3','1','1','1', '\3','1','1','2', '\3','1','1','3', '\3','1','1','4', + '\3','1','1','5', '\3','1','1','6', '\3','1','1','7', '\3','1','1','8', '\3','1','1','9', + '\3','1','2','0', '\3','1','2','1', '\3','1','2','2', '\3','1','2','3', '\3','1','2','4', + '\3','1','2','5', '\3','1','2','6', '\3','1','2','7', '\3','1','2','8', '\3','1','2','9', + '\3','1','3','0', '\3','1','3','1', '\3','1','3','2', '\3','1','3','3', '\3','1','3','4', + '\3','1','3','5', '\3','1','3','6', '\3','1','3','7', '\3','1','3','8', '\3','1','3','9', + '\3','1','4','0', '\3','1','4','1', '\3','1','4','2', '\3','1','4','3', '\3','1','4','4', + '\3','1','4','5', '\3','1','4','6', '\3','1','4','7', '\3','1','4','8', '\3','1','4','9', + '\3','1','5','0', '\3','1','5','1', '\3','1','5','2', '\3','1','5','3', '\3','1','5','4', + '\3','1','5','5', '\3','1','5','6', '\3','1','5','7', '\3','1','5','8', '\3','1','5','9', + '\3','1','6','0', '\3','1','6','1', '\3','1','6','2', '\3','1','6','3', '\3','1','6','4', + '\3','1','6','5', '\3','1','6','6', '\3','1','6','7', '\3','1','6','8', '\3','1','6','9', + '\3','1','7','0', '\3','1','7','1', '\3','1','7','2', '\3','1','7','3', '\3','1','7','4', + '\3','1','7','5', '\3','1','7','6', '\3','1','7','7', '\3','1','7','8', '\3','1','7','9', + '\3','1','8','0', '\3','1','8','1', '\3','1','8','2', '\3','1','8','3', '\3','1','8','4', + '\3','1','8','5', '\3','1','8','6', '\3','1','8','7', '\3','1','8','8', '\3','1','8','9', + '\3','1','9','0', '\3','1','9','1', '\3','1','9','2', '\3','1','9','3', '\3','1','9','4', + '\3','1','9','5', '\3','1','9','6', '\3','1','9','7', '\3','1','9','8', '\3','1','9','9', + '\3','2','0','0', '\3','2','0','1', '\3','2','0','2', '\3','2','0','3', '\3','2','0','4', + '\3','2','0','5', '\3','2','0','6', '\3','2','0','7', '\3','2','0','8', '\3','2','0','9', + '\3','2','1','0', '\3','2','1','1', '\3','2','1','2', '\3','2','1','3', '\3','2','1','4', + '\3','2','1','5', '\3','2','1','6', '\3','2','1','7', '\3','2','1','8', '\3','2','1','9', + '\3','2','2','0', '\3','2','2','1', '\3','2','2','2', '\3','2','2','3', '\3','2','2','4', + '\3','2','2','5', '\3','2','2','6', '\3','2','2','7', '\3','2','2','8', '\3','2','2','9', + '\3','2','3','0', '\3','2','3','1', '\3','2','3','2', '\3','2','3','3', '\3','2','3','4', + '\3','2','3','5', '\3','2','3','6', '\3','2','3','7', '\3','2','3','8', '\3','2','3','9', + '\3','2','4','0', '\3','2','4','1', '\3','2','4','2', '\3','2','4','3', '\3','2','4','4', + '\3','2','4','5', '\3','2','4','6', '\3','2','4','7', '\3','2','4','8', '\3','2','4','9', + '\3','2','5','0', '\3','2','5','1', '\3','2','5','2', '\3','2','5','3', '\3','2','5','4', + '\3','2','5','5', '\3','2','5','6', '\3','2','5','7', '\3','2','5','8', '\3','2','5','9', + '\3','2','6','0', '\3','2','6','1', '\3','2','6','2', '\3','2','6','3', '\3','2','6','4', + '\3','2','6','5', '\3','2','6','6', '\3','2','6','7', '\3','2','6','8', '\3','2','6','9', + '\3','2','7','0', '\3','2','7','1', '\3','2','7','2', '\3','2','7','3', '\3','2','7','4', + '\3','2','7','5', '\3','2','7','6', '\3','2','7','7', '\3','2','7','8', '\3','2','7','9', + '\3','2','8','0', '\3','2','8','1', '\3','2','8','2', '\3','2','8','3', '\3','2','8','4', + '\3','2','8','5', '\3','2','8','6', '\3','2','8','7', '\3','2','8','8', '\3','2','8','9', + '\3','2','9','0', '\3','2','9','1', '\3','2','9','2', '\3','2','9','3', '\3','2','9','4', + '\3','2','9','5', '\3','2','9','6', '\3','2','9','7', '\3','2','9','8', '\3','2','9','9', + '\3','3','0','0', '\3','3','0','1', '\3','3','0','2', '\3','3','0','3', '\3','3','0','4', + '\3','3','0','5', '\3','3','0','6', '\3','3','0','7', '\3','3','0','8', '\3','3','0','9', + '\3','3','1','0', '\3','3','1','1', '\3','3','1','2', '\3','3','1','3', '\3','3','1','4', + '\3','3','1','5', '\3','3','1','6', '\3','3','1','7', '\3','3','1','8', '\3','3','1','9', + '\3','3','2','0', '\3','3','2','1', '\3','3','2','2', '\3','3','2','3', '\3','3','2','4', + '\3','3','2','5', '\3','3','2','6', '\3','3','2','7', '\3','3','2','8', '\3','3','2','9', + '\3','3','3','0', '\3','3','3','1', '\3','3','3','2', '\3','3','3','3', '\3','3','3','4', + '\3','3','3','5', '\3','3','3','6', '\3','3','3','7', '\3','3','3','8', '\3','3','3','9', + '\3','3','4','0', '\3','3','4','1', '\3','3','4','2', '\3','3','4','3', '\3','3','4','4', + '\3','3','4','5', '\3','3','4','6', '\3','3','4','7', '\3','3','4','8', '\3','3','4','9', + '\3','3','5','0', '\3','3','5','1', '\3','3','5','2', '\3','3','5','3', '\3','3','5','4', + '\3','3','5','5', '\3','3','5','6', '\3','3','5','7', '\3','3','5','8', '\3','3','5','9', + '\3','3','6','0', '\3','3','6','1', '\3','3','6','2', '\3','3','6','3', '\3','3','6','4', + '\3','3','6','5', '\3','3','6','6', '\3','3','6','7', '\3','3','6','8', '\3','3','6','9', + '\3','3','7','0', '\3','3','7','1', '\3','3','7','2', '\3','3','7','3', '\3','3','7','4', + '\3','3','7','5', '\3','3','7','6', '\3','3','7','7', '\3','3','7','8', '\3','3','7','9', + '\3','3','8','0', '\3','3','8','1', '\3','3','8','2', '\3','3','8','3', '\3','3','8','4', + '\3','3','8','5', '\3','3','8','6', '\3','3','8','7', '\3','3','8','8', '\3','3','8','9', + '\3','3','9','0', '\3','3','9','1', '\3','3','9','2', '\3','3','9','3', '\3','3','9','4', + '\3','3','9','5', '\3','3','9','6', '\3','3','9','7', '\3','3','9','8', '\3','3','9','9', + '\3','4','0','0', '\3','4','0','1', '\3','4','0','2', '\3','4','0','3', '\3','4','0','4', + '\3','4','0','5', '\3','4','0','6', '\3','4','0','7', '\3','4','0','8', '\3','4','0','9', + '\3','4','1','0', '\3','4','1','1', '\3','4','1','2', '\3','4','1','3', '\3','4','1','4', + '\3','4','1','5', '\3','4','1','6', '\3','4','1','7', '\3','4','1','8', '\3','4','1','9', + '\3','4','2','0', '\3','4','2','1', '\3','4','2','2', '\3','4','2','3', '\3','4','2','4', + '\3','4','2','5', '\3','4','2','6', '\3','4','2','7', '\3','4','2','8', '\3','4','2','9', + '\3','4','3','0', '\3','4','3','1', '\3','4','3','2', '\3','4','3','3', '\3','4','3','4', + '\3','4','3','5', '\3','4','3','6', '\3','4','3','7', '\3','4','3','8', '\3','4','3','9', + '\3','4','4','0', '\3','4','4','1', '\3','4','4','2', '\3','4','4','3', '\3','4','4','4', + '\3','4','4','5', '\3','4','4','6', '\3','4','4','7', '\3','4','4','8', '\3','4','4','9', + '\3','4','5','0', '\3','4','5','1', '\3','4','5','2', '\3','4','5','3', '\3','4','5','4', + '\3','4','5','5', '\3','4','5','6', '\3','4','5','7', '\3','4','5','8', '\3','4','5','9', + '\3','4','6','0', '\3','4','6','1', '\3','4','6','2', '\3','4','6','3', '\3','4','6','4', + '\3','4','6','5', '\3','4','6','6', '\3','4','6','7', '\3','4','6','8', '\3','4','6','9', + '\3','4','7','0', '\3','4','7','1', '\3','4','7','2', '\3','4','7','3', '\3','4','7','4', + '\3','4','7','5', '\3','4','7','6', '\3','4','7','7', '\3','4','7','8', '\3','4','7','9', + '\3','4','8','0', '\3','4','8','1', '\3','4','8','2', '\3','4','8','3', '\3','4','8','4', + '\3','4','8','5', '\3','4','8','6', '\3','4','8','7', '\3','4','8','8', '\3','4','8','9', + '\3','4','9','0', '\3','4','9','1', '\3','4','9','2', '\3','4','9','3', '\3','4','9','4', + '\3','4','9','5', '\3','4','9','6', '\3','4','9','7', '\3','4','9','8', '\3','4','9','9', + '\3','5','0','0', '\3','5','0','1', '\3','5','0','2', '\3','5','0','3', '\3','5','0','4', + '\3','5','0','5', '\3','5','0','6', '\3','5','0','7', '\3','5','0','8', '\3','5','0','9', + '\3','5','1','0', '\3','5','1','1', '\3','5','1','2', '\3','5','1','3', '\3','5','1','4', + '\3','5','1','5', '\3','5','1','6', '\3','5','1','7', '\3','5','1','8', '\3','5','1','9', + '\3','5','2','0', '\3','5','2','1', '\3','5','2','2', '\3','5','2','3', '\3','5','2','4', + '\3','5','2','5', '\3','5','2','6', '\3','5','2','7', '\3','5','2','8', '\3','5','2','9', + '\3','5','3','0', '\3','5','3','1', '\3','5','3','2', '\3','5','3','3', '\3','5','3','4', + '\3','5','3','5', '\3','5','3','6', '\3','5','3','7', '\3','5','3','8', '\3','5','3','9', + '\3','5','4','0', '\3','5','4','1', '\3','5','4','2', '\3','5','4','3', '\3','5','4','4', + '\3','5','4','5', '\3','5','4','6', '\3','5','4','7', '\3','5','4','8', '\3','5','4','9', + '\3','5','5','0', '\3','5','5','1', '\3','5','5','2', '\3','5','5','3', '\3','5','5','4', + '\3','5','5','5', '\3','5','5','6', '\3','5','5','7', '\3','5','5','8', '\3','5','5','9', + '\3','5','6','0', '\3','5','6','1', '\3','5','6','2', '\3','5','6','3', '\3','5','6','4', + '\3','5','6','5', '\3','5','6','6', '\3','5','6','7', '\3','5','6','8', '\3','5','6','9', + '\3','5','7','0', '\3','5','7','1', '\3','5','7','2', '\3','5','7','3', '\3','5','7','4', + '\3','5','7','5', '\3','5','7','6', '\3','5','7','7', '\3','5','7','8', '\3','5','7','9', + '\3','5','8','0', '\3','5','8','1', '\3','5','8','2', '\3','5','8','3', '\3','5','8','4', + '\3','5','8','5', '\3','5','8','6', '\3','5','8','7', '\3','5','8','8', '\3','5','8','9', + '\3','5','9','0', '\3','5','9','1', '\3','5','9','2', '\3','5','9','3', '\3','5','9','4', + '\3','5','9','5', '\3','5','9','6', '\3','5','9','7', '\3','5','9','8', '\3','5','9','9', + '\3','6','0','0', '\3','6','0','1', '\3','6','0','2', '\3','6','0','3', '\3','6','0','4', + '\3','6','0','5', '\3','6','0','6', '\3','6','0','7', '\3','6','0','8', '\3','6','0','9', + '\3','6','1','0', '\3','6','1','1', '\3','6','1','2', '\3','6','1','3', '\3','6','1','4', + '\3','6','1','5', '\3','6','1','6', '\3','6','1','7', '\3','6','1','8', '\3','6','1','9', + '\3','6','2','0', '\3','6','2','1', '\3','6','2','2', '\3','6','2','3', '\3','6','2','4', + '\3','6','2','5', '\3','6','2','6', '\3','6','2','7', '\3','6','2','8', '\3','6','2','9', + '\3','6','3','0', '\3','6','3','1', '\3','6','3','2', '\3','6','3','3', '\3','6','3','4', + '\3','6','3','5', '\3','6','3','6', '\3','6','3','7', '\3','6','3','8', '\3','6','3','9', + '\3','6','4','0', '\3','6','4','1', '\3','6','4','2', '\3','6','4','3', '\3','6','4','4', + '\3','6','4','5', '\3','6','4','6', '\3','6','4','7', '\3','6','4','8', '\3','6','4','9', + '\3','6','5','0', '\3','6','5','1', '\3','6','5','2', '\3','6','5','3', '\3','6','5','4', + '\3','6','5','5', '\3','6','5','6', '\3','6','5','7', '\3','6','5','8', '\3','6','5','9', + '\3','6','6','0', '\3','6','6','1', '\3','6','6','2', '\3','6','6','3', '\3','6','6','4', + '\3','6','6','5', '\3','6','6','6', '\3','6','6','7', '\3','6','6','8', '\3','6','6','9', + '\3','6','7','0', '\3','6','7','1', '\3','6','7','2', '\3','6','7','3', '\3','6','7','4', + '\3','6','7','5', '\3','6','7','6', '\3','6','7','7', '\3','6','7','8', '\3','6','7','9', + '\3','6','8','0', '\3','6','8','1', '\3','6','8','2', '\3','6','8','3', '\3','6','8','4', + '\3','6','8','5', '\3','6','8','6', '\3','6','8','7', '\3','6','8','8', '\3','6','8','9', + '\3','6','9','0', '\3','6','9','1', '\3','6','9','2', '\3','6','9','3', '\3','6','9','4', + '\3','6','9','5', '\3','6','9','6', '\3','6','9','7', '\3','6','9','8', '\3','6','9','9', + '\3','7','0','0', '\3','7','0','1', '\3','7','0','2', '\3','7','0','3', '\3','7','0','4', + '\3','7','0','5', '\3','7','0','6', '\3','7','0','7', '\3','7','0','8', '\3','7','0','9', + '\3','7','1','0', '\3','7','1','1', '\3','7','1','2', '\3','7','1','3', '\3','7','1','4', + '\3','7','1','5', '\3','7','1','6', '\3','7','1','7', '\3','7','1','8', '\3','7','1','9', + '\3','7','2','0', '\3','7','2','1', '\3','7','2','2', '\3','7','2','3', '\3','7','2','4', + '\3','7','2','5', '\3','7','2','6', '\3','7','2','7', '\3','7','2','8', '\3','7','2','9', + '\3','7','3','0', '\3','7','3','1', '\3','7','3','2', '\3','7','3','3', '\3','7','3','4', + '\3','7','3','5', '\3','7','3','6', '\3','7','3','7', '\3','7','3','8', '\3','7','3','9', + '\3','7','4','0', '\3','7','4','1', '\3','7','4','2', '\3','7','4','3', '\3','7','4','4', + '\3','7','4','5', '\3','7','4','6', '\3','7','4','7', '\3','7','4','8', '\3','7','4','9', + '\3','7','5','0', '\3','7','5','1', '\3','7','5','2', '\3','7','5','3', '\3','7','5','4', + '\3','7','5','5', '\3','7','5','6', '\3','7','5','7', '\3','7','5','8', '\3','7','5','9', + '\3','7','6','0', '\3','7','6','1', '\3','7','6','2', '\3','7','6','3', '\3','7','6','4', + '\3','7','6','5', '\3','7','6','6', '\3','7','6','7', '\3','7','6','8', '\3','7','6','9', + '\3','7','7','0', '\3','7','7','1', '\3','7','7','2', '\3','7','7','3', '\3','7','7','4', + '\3','7','7','5', '\3','7','7','6', '\3','7','7','7', '\3','7','7','8', '\3','7','7','9', + '\3','7','8','0', '\3','7','8','1', '\3','7','8','2', '\3','7','8','3', '\3','7','8','4', + '\3','7','8','5', '\3','7','8','6', '\3','7','8','7', '\3','7','8','8', '\3','7','8','9', + '\3','7','9','0', '\3','7','9','1', '\3','7','9','2', '\3','7','9','3', '\3','7','9','4', + '\3','7','9','5', '\3','7','9','6', '\3','7','9','7', '\3','7','9','8', '\3','7','9','9', + '\3','8','0','0', '\3','8','0','1', '\3','8','0','2', '\3','8','0','3', '\3','8','0','4', + '\3','8','0','5', '\3','8','0','6', '\3','8','0','7', '\3','8','0','8', '\3','8','0','9', + '\3','8','1','0', '\3','8','1','1', '\3','8','1','2', '\3','8','1','3', '\3','8','1','4', + '\3','8','1','5', '\3','8','1','6', '\3','8','1','7', '\3','8','1','8', '\3','8','1','9', + '\3','8','2','0', '\3','8','2','1', '\3','8','2','2', '\3','8','2','3', '\3','8','2','4', + '\3','8','2','5', '\3','8','2','6', '\3','8','2','7', '\3','8','2','8', '\3','8','2','9', + '\3','8','3','0', '\3','8','3','1', '\3','8','3','2', '\3','8','3','3', '\3','8','3','4', + '\3','8','3','5', '\3','8','3','6', '\3','8','3','7', '\3','8','3','8', '\3','8','3','9', + '\3','8','4','0', '\3','8','4','1', '\3','8','4','2', '\3','8','4','3', '\3','8','4','4', + '\3','8','4','5', '\3','8','4','6', '\3','8','4','7', '\3','8','4','8', '\3','8','4','9', + '\3','8','5','0', '\3','8','5','1', '\3','8','5','2', '\3','8','5','3', '\3','8','5','4', + '\3','8','5','5', '\3','8','5','6', '\3','8','5','7', '\3','8','5','8', '\3','8','5','9', + '\3','8','6','0', '\3','8','6','1', '\3','8','6','2', '\3','8','6','3', '\3','8','6','4', + '\3','8','6','5', '\3','8','6','6', '\3','8','6','7', '\3','8','6','8', '\3','8','6','9', + '\3','8','7','0', '\3','8','7','1', '\3','8','7','2', '\3','8','7','3', '\3','8','7','4', + '\3','8','7','5', '\3','8','7','6', '\3','8','7','7', '\3','8','7','8', '\3','8','7','9', + '\3','8','8','0', '\3','8','8','1', '\3','8','8','2', '\3','8','8','3', '\3','8','8','4', + '\3','8','8','5', '\3','8','8','6', '\3','8','8','7', '\3','8','8','8', '\3','8','8','9', + '\3','8','9','0', '\3','8','9','1', '\3','8','9','2', '\3','8','9','3', '\3','8','9','4', + '\3','8','9','5', '\3','8','9','6', '\3','8','9','7', '\3','8','9','8', '\3','8','9','9', + '\3','9','0','0', '\3','9','0','1', '\3','9','0','2', '\3','9','0','3', '\3','9','0','4', + '\3','9','0','5', '\3','9','0','6', '\3','9','0','7', '\3','9','0','8', '\3','9','0','9', + '\3','9','1','0', '\3','9','1','1', '\3','9','1','2', '\3','9','1','3', '\3','9','1','4', + '\3','9','1','5', '\3','9','1','6', '\3','9','1','7', '\3','9','1','8', '\3','9','1','9', + '\3','9','2','0', '\3','9','2','1', '\3','9','2','2', '\3','9','2','3', '\3','9','2','4', + '\3','9','2','5', '\3','9','2','6', '\3','9','2','7', '\3','9','2','8', '\3','9','2','9', + '\3','9','3','0', '\3','9','3','1', '\3','9','3','2', '\3','9','3','3', '\3','9','3','4', + '\3','9','3','5', '\3','9','3','6', '\3','9','3','7', '\3','9','3','8', '\3','9','3','9', + '\3','9','4','0', '\3','9','4','1', '\3','9','4','2', '\3','9','4','3', '\3','9','4','4', + '\3','9','4','5', '\3','9','4','6', '\3','9','4','7', '\3','9','4','8', '\3','9','4','9', + '\3','9','5','0', '\3','9','5','1', '\3','9','5','2', '\3','9','5','3', '\3','9','5','4', + '\3','9','5','5', '\3','9','5','6', '\3','9','5','7', '\3','9','5','8', '\3','9','5','9', + '\3','9','6','0', '\3','9','6','1', '\3','9','6','2', '\3','9','6','3', '\3','9','6','4', + '\3','9','6','5', '\3','9','6','6', '\3','9','6','7', '\3','9','6','8', '\3','9','6','9', + '\3','9','7','0', '\3','9','7','1', '\3','9','7','2', '\3','9','7','3', '\3','9','7','4', + '\3','9','7','5', '\3','9','7','6', '\3','9','7','7', '\3','9','7','8', '\3','9','7','9', + '\3','9','8','0', '\3','9','8','1', '\3','9','8','2', '\3','9','8','3', '\3','9','8','4', + '\3','9','8','5', '\3','9','8','6', '\3','9','8','7', '\3','9','8','8', '\3','9','8','9', + '\3','9','9','0', '\3','9','9','1', '\3','9','9','2', '\3','9','9','3', '\3','9','9','4', + '\3','9','9','5', '\3','9','9','6', '\3','9','9','7', '\3','9','9','8', '\3','9','9','9', '\0'}; +#endif + +#if defined(DEC_DPD2BCD8) && DEC_DPD2BCD8==1 && !defined(DECDPD2BCD8) +#define DECDPD2BCD8 + +const uint8_t DPD2BCD8[4096]={ + 0,0,0,0, 0,0,1,1, 0,0,2,1, 0,0,3,1, 0,0,4,1, 0,0,5,1, 0,0,6,1, 0,0,7,1, 0,0,8,1, + 0,0,9,1, 0,8,0,2, 0,8,1,2, 8,0,0,3, 8,0,1,3, 8,8,0,3, 8,8,1,3, 0,1,0,2, 0,1,1,2, + 0,1,2,2, 0,1,3,2, 0,1,4,2, 0,1,5,2, 0,1,6,2, 0,1,7,2, 0,1,8,2, 0,1,9,2, 0,9,0,2, + 0,9,1,2, 8,1,0,3, 8,1,1,3, 8,9,0,3, 8,9,1,3, 0,2,0,2, 0,2,1,2, 0,2,2,2, 0,2,3,2, + 0,2,4,2, 0,2,5,2, 0,2,6,2, 0,2,7,2, 0,2,8,2, 0,2,9,2, 0,8,2,2, 0,8,3,2, 8,2,0,3, + 8,2,1,3, 8,0,8,3, 8,0,9,3, 0,3,0,2, 0,3,1,2, 0,3,2,2, 0,3,3,2, 0,3,4,2, 0,3,5,2, + 0,3,6,2, 0,3,7,2, 0,3,8,2, 0,3,9,2, 0,9,2,2, 0,9,3,2, 8,3,0,3, 8,3,1,3, 8,1,8,3, + 8,1,9,3, 0,4,0,2, 0,4,1,2, 0,4,2,2, 0,4,3,2, 0,4,4,2, 0,4,5,2, 0,4,6,2, 0,4,7,2, + 0,4,8,2, 0,4,9,2, 0,8,4,2, 0,8,5,2, 8,4,0,3, 8,4,1,3, 0,8,8,2, 0,8,9,2, 0,5,0,2, + 0,5,1,2, 0,5,2,2, 0,5,3,2, 0,5,4,2, 0,5,5,2, 0,5,6,2, 0,5,7,2, 0,5,8,2, 0,5,9,2, + 0,9,4,2, 0,9,5,2, 8,5,0,3, 8,5,1,3, 0,9,8,2, 0,9,9,2, 0,6,0,2, 0,6,1,2, 0,6,2,2, + 0,6,3,2, 0,6,4,2, 0,6,5,2, 0,6,6,2, 0,6,7,2, 0,6,8,2, 0,6,9,2, 0,8,6,2, 0,8,7,2, + 8,6,0,3, 8,6,1,3, 8,8,8,3, 8,8,9,3, 0,7,0,2, 0,7,1,2, 0,7,2,2, 0,7,3,2, 0,7,4,2, + 0,7,5,2, 0,7,6,2, 0,7,7,2, 0,7,8,2, 0,7,9,2, 0,9,6,2, 0,9,7,2, 8,7,0,3, 8,7,1,3, + 8,9,8,3, 8,9,9,3, 1,0,0,3, 1,0,1,3, 1,0,2,3, 1,0,3,3, 1,0,4,3, 1,0,5,3, 1,0,6,3, + 1,0,7,3, 1,0,8,3, 1,0,9,3, 1,8,0,3, 1,8,1,3, 9,0,0,3, 9,0,1,3, 9,8,0,3, 9,8,1,3, + 1,1,0,3, 1,1,1,3, 1,1,2,3, 1,1,3,3, 1,1,4,3, 1,1,5,3, 1,1,6,3, 1,1,7,3, 1,1,8,3, + 1,1,9,3, 1,9,0,3, 1,9,1,3, 9,1,0,3, 9,1,1,3, 9,9,0,3, 9,9,1,3, 1,2,0,3, 1,2,1,3, + 1,2,2,3, 1,2,3,3, 1,2,4,3, 1,2,5,3, 1,2,6,3, 1,2,7,3, 1,2,8,3, 1,2,9,3, 1,8,2,3, + 1,8,3,3, 9,2,0,3, 9,2,1,3, 9,0,8,3, 9,0,9,3, 1,3,0,3, 1,3,1,3, 1,3,2,3, 1,3,3,3, + 1,3,4,3, 1,3,5,3, 1,3,6,3, 1,3,7,3, 1,3,8,3, 1,3,9,3, 1,9,2,3, 1,9,3,3, 9,3,0,3, + 9,3,1,3, 9,1,8,3, 9,1,9,3, 1,4,0,3, 1,4,1,3, 1,4,2,3, 1,4,3,3, 1,4,4,3, 1,4,5,3, + 1,4,6,3, 1,4,7,3, 1,4,8,3, 1,4,9,3, 1,8,4,3, 1,8,5,3, 9,4,0,3, 9,4,1,3, 1,8,8,3, + 1,8,9,3, 1,5,0,3, 1,5,1,3, 1,5,2,3, 1,5,3,3, 1,5,4,3, 1,5,5,3, 1,5,6,3, 1,5,7,3, + 1,5,8,3, 1,5,9,3, 1,9,4,3, 1,9,5,3, 9,5,0,3, 9,5,1,3, 1,9,8,3, 1,9,9,3, 1,6,0,3, + 1,6,1,3, 1,6,2,3, 1,6,3,3, 1,6,4,3, 1,6,5,3, 1,6,6,3, 1,6,7,3, 1,6,8,3, 1,6,9,3, + 1,8,6,3, 1,8,7,3, 9,6,0,3, 9,6,1,3, 9,8,8,3, 9,8,9,3, 1,7,0,3, 1,7,1,3, 1,7,2,3, + 1,7,3,3, 1,7,4,3, 1,7,5,3, 1,7,6,3, 1,7,7,3, 1,7,8,3, 1,7,9,3, 1,9,6,3, 1,9,7,3, + 9,7,0,3, 9,7,1,3, 9,9,8,3, 9,9,9,3, 2,0,0,3, 2,0,1,3, 2,0,2,3, 2,0,3,3, 2,0,4,3, + 2,0,5,3, 2,0,6,3, 2,0,7,3, 2,0,8,3, 2,0,9,3, 2,8,0,3, 2,8,1,3, 8,0,2,3, 8,0,3,3, + 8,8,2,3, 8,8,3,3, 2,1,0,3, 2,1,1,3, 2,1,2,3, 2,1,3,3, 2,1,4,3, 2,1,5,3, 2,1,6,3, + 2,1,7,3, 2,1,8,3, 2,1,9,3, 2,9,0,3, 2,9,1,3, 8,1,2,3, 8,1,3,3, 8,9,2,3, 8,9,3,3, + 2,2,0,3, 2,2,1,3, 2,2,2,3, 2,2,3,3, 2,2,4,3, 2,2,5,3, 2,2,6,3, 2,2,7,3, 2,2,8,3, + 2,2,9,3, 2,8,2,3, 2,8,3,3, 8,2,2,3, 8,2,3,3, 8,2,8,3, 8,2,9,3, 2,3,0,3, 2,3,1,3, + 2,3,2,3, 2,3,3,3, 2,3,4,3, 2,3,5,3, 2,3,6,3, 2,3,7,3, 2,3,8,3, 2,3,9,3, 2,9,2,3, + 2,9,3,3, 8,3,2,3, 8,3,3,3, 8,3,8,3, 8,3,9,3, 2,4,0,3, 2,4,1,3, 2,4,2,3, 2,4,3,3, + 2,4,4,3, 2,4,5,3, 2,4,6,3, 2,4,7,3, 2,4,8,3, 2,4,9,3, 2,8,4,3, 2,8,5,3, 8,4,2,3, + 8,4,3,3, 2,8,8,3, 2,8,9,3, 2,5,0,3, 2,5,1,3, 2,5,2,3, 2,5,3,3, 2,5,4,3, 2,5,5,3, + 2,5,6,3, 2,5,7,3, 2,5,8,3, 2,5,9,3, 2,9,4,3, 2,9,5,3, 8,5,2,3, 8,5,3,3, 2,9,8,3, + 2,9,9,3, 2,6,0,3, 2,6,1,3, 2,6,2,3, 2,6,3,3, 2,6,4,3, 2,6,5,3, 2,6,6,3, 2,6,7,3, + 2,6,8,3, 2,6,9,3, 2,8,6,3, 2,8,7,3, 8,6,2,3, 8,6,3,3, 8,8,8,3, 8,8,9,3, 2,7,0,3, + 2,7,1,3, 2,7,2,3, 2,7,3,3, 2,7,4,3, 2,7,5,3, 2,7,6,3, 2,7,7,3, 2,7,8,3, 2,7,9,3, + 2,9,6,3, 2,9,7,3, 8,7,2,3, 8,7,3,3, 8,9,8,3, 8,9,9,3, 3,0,0,3, 3,0,1,3, 3,0,2,3, + 3,0,3,3, 3,0,4,3, 3,0,5,3, 3,0,6,3, 3,0,7,3, 3,0,8,3, 3,0,9,3, 3,8,0,3, 3,8,1,3, + 9,0,2,3, 9,0,3,3, 9,8,2,3, 9,8,3,3, 3,1,0,3, 3,1,1,3, 3,1,2,3, 3,1,3,3, 3,1,4,3, + 3,1,5,3, 3,1,6,3, 3,1,7,3, 3,1,8,3, 3,1,9,3, 3,9,0,3, 3,9,1,3, 9,1,2,3, 9,1,3,3, + 9,9,2,3, 9,9,3,3, 3,2,0,3, 3,2,1,3, 3,2,2,3, 3,2,3,3, 3,2,4,3, 3,2,5,3, 3,2,6,3, + 3,2,7,3, 3,2,8,3, 3,2,9,3, 3,8,2,3, 3,8,3,3, 9,2,2,3, 9,2,3,3, 9,2,8,3, 9,2,9,3, + 3,3,0,3, 3,3,1,3, 3,3,2,3, 3,3,3,3, 3,3,4,3, 3,3,5,3, 3,3,6,3, 3,3,7,3, 3,3,8,3, + 3,3,9,3, 3,9,2,3, 3,9,3,3, 9,3,2,3, 9,3,3,3, 9,3,8,3, 9,3,9,3, 3,4,0,3, 3,4,1,3, + 3,4,2,3, 3,4,3,3, 3,4,4,3, 3,4,5,3, 3,4,6,3, 3,4,7,3, 3,4,8,3, 3,4,9,3, 3,8,4,3, + 3,8,5,3, 9,4,2,3, 9,4,3,3, 3,8,8,3, 3,8,9,3, 3,5,0,3, 3,5,1,3, 3,5,2,3, 3,5,3,3, + 3,5,4,3, 3,5,5,3, 3,5,6,3, 3,5,7,3, 3,5,8,3, 3,5,9,3, 3,9,4,3, 3,9,5,3, 9,5,2,3, + 9,5,3,3, 3,9,8,3, 3,9,9,3, 3,6,0,3, 3,6,1,3, 3,6,2,3, 3,6,3,3, 3,6,4,3, 3,6,5,3, + 3,6,6,3, 3,6,7,3, 3,6,8,3, 3,6,9,3, 3,8,6,3, 3,8,7,3, 9,6,2,3, 9,6,3,3, 9,8,8,3, + 9,8,9,3, 3,7,0,3, 3,7,1,3, 3,7,2,3, 3,7,3,3, 3,7,4,3, 3,7,5,3, 3,7,6,3, 3,7,7,3, + 3,7,8,3, 3,7,9,3, 3,9,6,3, 3,9,7,3, 9,7,2,3, 9,7,3,3, 9,9,8,3, 9,9,9,3, 4,0,0,3, + 4,0,1,3, 4,0,2,3, 4,0,3,3, 4,0,4,3, 4,0,5,3, 4,0,6,3, 4,0,7,3, 4,0,8,3, 4,0,9,3, + 4,8,0,3, 4,8,1,3, 8,0,4,3, 8,0,5,3, 8,8,4,3, 8,8,5,3, 4,1,0,3, 4,1,1,3, 4,1,2,3, + 4,1,3,3, 4,1,4,3, 4,1,5,3, 4,1,6,3, 4,1,7,3, 4,1,8,3, 4,1,9,3, 4,9,0,3, 4,9,1,3, + 8,1,4,3, 8,1,5,3, 8,9,4,3, 8,9,5,3, 4,2,0,3, 4,2,1,3, 4,2,2,3, 4,2,3,3, 4,2,4,3, + 4,2,5,3, 4,2,6,3, 4,2,7,3, 4,2,8,3, 4,2,9,3, 4,8,2,3, 4,8,3,3, 8,2,4,3, 8,2,5,3, + 8,4,8,3, 8,4,9,3, 4,3,0,3, 4,3,1,3, 4,3,2,3, 4,3,3,3, 4,3,4,3, 4,3,5,3, 4,3,6,3, + 4,3,7,3, 4,3,8,3, 4,3,9,3, 4,9,2,3, 4,9,3,3, 8,3,4,3, 8,3,5,3, 8,5,8,3, 8,5,9,3, + 4,4,0,3, 4,4,1,3, 4,4,2,3, 4,4,3,3, 4,4,4,3, 4,4,5,3, 4,4,6,3, 4,4,7,3, 4,4,8,3, + 4,4,9,3, 4,8,4,3, 4,8,5,3, 8,4,4,3, 8,4,5,3, 4,8,8,3, 4,8,9,3, 4,5,0,3, 4,5,1,3, + 4,5,2,3, 4,5,3,3, 4,5,4,3, 4,5,5,3, 4,5,6,3, 4,5,7,3, 4,5,8,3, 4,5,9,3, 4,9,4,3, + 4,9,5,3, 8,5,4,3, 8,5,5,3, 4,9,8,3, 4,9,9,3, 4,6,0,3, 4,6,1,3, 4,6,2,3, 4,6,3,3, + 4,6,4,3, 4,6,5,3, 4,6,6,3, 4,6,7,3, 4,6,8,3, 4,6,9,3, 4,8,6,3, 4,8,7,3, 8,6,4,3, + 8,6,5,3, 8,8,8,3, 8,8,9,3, 4,7,0,3, 4,7,1,3, 4,7,2,3, 4,7,3,3, 4,7,4,3, 4,7,5,3, + 4,7,6,3, 4,7,7,3, 4,7,8,3, 4,7,9,3, 4,9,6,3, 4,9,7,3, 8,7,4,3, 8,7,5,3, 8,9,8,3, + 8,9,9,3, 5,0,0,3, 5,0,1,3, 5,0,2,3, 5,0,3,3, 5,0,4,3, 5,0,5,3, 5,0,6,3, 5,0,7,3, + 5,0,8,3, 5,0,9,3, 5,8,0,3, 5,8,1,3, 9,0,4,3, 9,0,5,3, 9,8,4,3, 9,8,5,3, 5,1,0,3, + 5,1,1,3, 5,1,2,3, 5,1,3,3, 5,1,4,3, 5,1,5,3, 5,1,6,3, 5,1,7,3, 5,1,8,3, 5,1,9,3, + 5,9,0,3, 5,9,1,3, 9,1,4,3, 9,1,5,3, 9,9,4,3, 9,9,5,3, 5,2,0,3, 5,2,1,3, 5,2,2,3, + 5,2,3,3, 5,2,4,3, 5,2,5,3, 5,2,6,3, 5,2,7,3, 5,2,8,3, 5,2,9,3, 5,8,2,3, 5,8,3,3, + 9,2,4,3, 9,2,5,3, 9,4,8,3, 9,4,9,3, 5,3,0,3, 5,3,1,3, 5,3,2,3, 5,3,3,3, 5,3,4,3, + 5,3,5,3, 5,3,6,3, 5,3,7,3, 5,3,8,3, 5,3,9,3, 5,9,2,3, 5,9,3,3, 9,3,4,3, 9,3,5,3, + 9,5,8,3, 9,5,9,3, 5,4,0,3, 5,4,1,3, 5,4,2,3, 5,4,3,3, 5,4,4,3, 5,4,5,3, 5,4,6,3, + 5,4,7,3, 5,4,8,3, 5,4,9,3, 5,8,4,3, 5,8,5,3, 9,4,4,3, 9,4,5,3, 5,8,8,3, 5,8,9,3, + 5,5,0,3, 5,5,1,3, 5,5,2,3, 5,5,3,3, 5,5,4,3, 5,5,5,3, 5,5,6,3, 5,5,7,3, 5,5,8,3, + 5,5,9,3, 5,9,4,3, 5,9,5,3, 9,5,4,3, 9,5,5,3, 5,9,8,3, 5,9,9,3, 5,6,0,3, 5,6,1,3, + 5,6,2,3, 5,6,3,3, 5,6,4,3, 5,6,5,3, 5,6,6,3, 5,6,7,3, 5,6,8,3, 5,6,9,3, 5,8,6,3, + 5,8,7,3, 9,6,4,3, 9,6,5,3, 9,8,8,3, 9,8,9,3, 5,7,0,3, 5,7,1,3, 5,7,2,3, 5,7,3,3, + 5,7,4,3, 5,7,5,3, 5,7,6,3, 5,7,7,3, 5,7,8,3, 5,7,9,3, 5,9,6,3, 5,9,7,3, 9,7,4,3, + 9,7,5,3, 9,9,8,3, 9,9,9,3, 6,0,0,3, 6,0,1,3, 6,0,2,3, 6,0,3,3, 6,0,4,3, 6,0,5,3, + 6,0,6,3, 6,0,7,3, 6,0,8,3, 6,0,9,3, 6,8,0,3, 6,8,1,3, 8,0,6,3, 8,0,7,3, 8,8,6,3, + 8,8,7,3, 6,1,0,3, 6,1,1,3, 6,1,2,3, 6,1,3,3, 6,1,4,3, 6,1,5,3, 6,1,6,3, 6,1,7,3, + 6,1,8,3, 6,1,9,3, 6,9,0,3, 6,9,1,3, 8,1,6,3, 8,1,7,3, 8,9,6,3, 8,9,7,3, 6,2,0,3, + 6,2,1,3, 6,2,2,3, 6,2,3,3, 6,2,4,3, 6,2,5,3, 6,2,6,3, 6,2,7,3, 6,2,8,3, 6,2,9,3, + 6,8,2,3, 6,8,3,3, 8,2,6,3, 8,2,7,3, 8,6,8,3, 8,6,9,3, 6,3,0,3, 6,3,1,3, 6,3,2,3, + 6,3,3,3, 6,3,4,3, 6,3,5,3, 6,3,6,3, 6,3,7,3, 6,3,8,3, 6,3,9,3, 6,9,2,3, 6,9,3,3, + 8,3,6,3, 8,3,7,3, 8,7,8,3, 8,7,9,3, 6,4,0,3, 6,4,1,3, 6,4,2,3, 6,4,3,3, 6,4,4,3, + 6,4,5,3, 6,4,6,3, 6,4,7,3, 6,4,8,3, 6,4,9,3, 6,8,4,3, 6,8,5,3, 8,4,6,3, 8,4,7,3, + 6,8,8,3, 6,8,9,3, 6,5,0,3, 6,5,1,3, 6,5,2,3, 6,5,3,3, 6,5,4,3, 6,5,5,3, 6,5,6,3, + 6,5,7,3, 6,5,8,3, 6,5,9,3, 6,9,4,3, 6,9,5,3, 8,5,6,3, 8,5,7,3, 6,9,8,3, 6,9,9,3, + 6,6,0,3, 6,6,1,3, 6,6,2,3, 6,6,3,3, 6,6,4,3, 6,6,5,3, 6,6,6,3, 6,6,7,3, 6,6,8,3, + 6,6,9,3, 6,8,6,3, 6,8,7,3, 8,6,6,3, 8,6,7,3, 8,8,8,3, 8,8,9,3, 6,7,0,3, 6,7,1,3, + 6,7,2,3, 6,7,3,3, 6,7,4,3, 6,7,5,3, 6,7,6,3, 6,7,7,3, 6,7,8,3, 6,7,9,3, 6,9,6,3, + 6,9,7,3, 8,7,6,3, 8,7,7,3, 8,9,8,3, 8,9,9,3, 7,0,0,3, 7,0,1,3, 7,0,2,3, 7,0,3,3, + 7,0,4,3, 7,0,5,3, 7,0,6,3, 7,0,7,3, 7,0,8,3, 7,0,9,3, 7,8,0,3, 7,8,1,3, 9,0,6,3, + 9,0,7,3, 9,8,6,3, 9,8,7,3, 7,1,0,3, 7,1,1,3, 7,1,2,3, 7,1,3,3, 7,1,4,3, 7,1,5,3, + 7,1,6,3, 7,1,7,3, 7,1,8,3, 7,1,9,3, 7,9,0,3, 7,9,1,3, 9,1,6,3, 9,1,7,3, 9,9,6,3, + 9,9,7,3, 7,2,0,3, 7,2,1,3, 7,2,2,3, 7,2,3,3, 7,2,4,3, 7,2,5,3, 7,2,6,3, 7,2,7,3, + 7,2,8,3, 7,2,9,3, 7,8,2,3, 7,8,3,3, 9,2,6,3, 9,2,7,3, 9,6,8,3, 9,6,9,3, 7,3,0,3, + 7,3,1,3, 7,3,2,3, 7,3,3,3, 7,3,4,3, 7,3,5,3, 7,3,6,3, 7,3,7,3, 7,3,8,3, 7,3,9,3, + 7,9,2,3, 7,9,3,3, 9,3,6,3, 9,3,7,3, 9,7,8,3, 9,7,9,3, 7,4,0,3, 7,4,1,3, 7,4,2,3, + 7,4,3,3, 7,4,4,3, 7,4,5,3, 7,4,6,3, 7,4,7,3, 7,4,8,3, 7,4,9,3, 7,8,4,3, 7,8,5,3, + 9,4,6,3, 9,4,7,3, 7,8,8,3, 7,8,9,3, 7,5,0,3, 7,5,1,3, 7,5,2,3, 7,5,3,3, 7,5,4,3, + 7,5,5,3, 7,5,6,3, 7,5,7,3, 7,5,8,3, 7,5,9,3, 7,9,4,3, 7,9,5,3, 9,5,6,3, 9,5,7,3, + 7,9,8,3, 7,9,9,3, 7,6,0,3, 7,6,1,3, 7,6,2,3, 7,6,3,3, 7,6,4,3, 7,6,5,3, 7,6,6,3, + 7,6,7,3, 7,6,8,3, 7,6,9,3, 7,8,6,3, 7,8,7,3, 9,6,6,3, 9,6,7,3, 9,8,8,3, 9,8,9,3, + 7,7,0,3, 7,7,1,3, 7,7,2,3, 7,7,3,3, 7,7,4,3, 7,7,5,3, 7,7,6,3, 7,7,7,3, 7,7,8,3, + 7,7,9,3, 7,9,6,3, 7,9,7,3, 9,7,6,3, 9,7,7,3, 9,9,8,3, 9,9,9,3}; +#endif + +#if defined(DEC_BIN2BCD8) && DEC_BIN2BCD8==1 && !defined(DECBIN2BCD8) +#define DECBIN2BCD8 + +const uint8_t BIN2BCD8[4000]={ + 0,0,0,0, 0,0,1,1, 0,0,2,1, 0,0,3,1, 0,0,4,1, 0,0,5,1, 0,0,6,1, 0,0,7,1, 0,0,8,1, + 0,0,9,1, 0,1,0,2, 0,1,1,2, 0,1,2,2, 0,1,3,2, 0,1,4,2, 0,1,5,2, 0,1,6,2, 0,1,7,2, + 0,1,8,2, 0,1,9,2, 0,2,0,2, 0,2,1,2, 0,2,2,2, 0,2,3,2, 0,2,4,2, 0,2,5,2, 0,2,6,2, + 0,2,7,2, 0,2,8,2, 0,2,9,2, 0,3,0,2, 0,3,1,2, 0,3,2,2, 0,3,3,2, 0,3,4,2, 0,3,5,2, + 0,3,6,2, 0,3,7,2, 0,3,8,2, 0,3,9,2, 0,4,0,2, 0,4,1,2, 0,4,2,2, 0,4,3,2, 0,4,4,2, + 0,4,5,2, 0,4,6,2, 0,4,7,2, 0,4,8,2, 0,4,9,2, 0,5,0,2, 0,5,1,2, 0,5,2,2, 0,5,3,2, + 0,5,4,2, 0,5,5,2, 0,5,6,2, 0,5,7,2, 0,5,8,2, 0,5,9,2, 0,6,0,2, 0,6,1,2, 0,6,2,2, + 0,6,3,2, 0,6,4,2, 0,6,5,2, 0,6,6,2, 0,6,7,2, 0,6,8,2, 0,6,9,2, 0,7,0,2, 0,7,1,2, + 0,7,2,2, 0,7,3,2, 0,7,4,2, 0,7,5,2, 0,7,6,2, 0,7,7,2, 0,7,8,2, 0,7,9,2, 0,8,0,2, + 0,8,1,2, 0,8,2,2, 0,8,3,2, 0,8,4,2, 0,8,5,2, 0,8,6,2, 0,8,7,2, 0,8,8,2, 0,8,9,2, + 0,9,0,2, 0,9,1,2, 0,9,2,2, 0,9,3,2, 0,9,4,2, 0,9,5,2, 0,9,6,2, 0,9,7,2, 0,9,8,2, + 0,9,9,2, 1,0,0,3, 1,0,1,3, 1,0,2,3, 1,0,3,3, 1,0,4,3, 1,0,5,3, 1,0,6,3, 1,0,7,3, + 1,0,8,3, 1,0,9,3, 1,1,0,3, 1,1,1,3, 1,1,2,3, 1,1,3,3, 1,1,4,3, 1,1,5,3, 1,1,6,3, + 1,1,7,3, 1,1,8,3, 1,1,9,3, 1,2,0,3, 1,2,1,3, 1,2,2,3, 1,2,3,3, 1,2,4,3, 1,2,5,3, + 1,2,6,3, 1,2,7,3, 1,2,8,3, 1,2,9,3, 1,3,0,3, 1,3,1,3, 1,3,2,3, 1,3,3,3, 1,3,4,3, + 1,3,5,3, 1,3,6,3, 1,3,7,3, 1,3,8,3, 1,3,9,3, 1,4,0,3, 1,4,1,3, 1,4,2,3, 1,4,3,3, + 1,4,4,3, 1,4,5,3, 1,4,6,3, 1,4,7,3, 1,4,8,3, 1,4,9,3, 1,5,0,3, 1,5,1,3, 1,5,2,3, + 1,5,3,3, 1,5,4,3, 1,5,5,3, 1,5,6,3, 1,5,7,3, 1,5,8,3, 1,5,9,3, 1,6,0,3, 1,6,1,3, + 1,6,2,3, 1,6,3,3, 1,6,4,3, 1,6,5,3, 1,6,6,3, 1,6,7,3, 1,6,8,3, 1,6,9,3, 1,7,0,3, + 1,7,1,3, 1,7,2,3, 1,7,3,3, 1,7,4,3, 1,7,5,3, 1,7,6,3, 1,7,7,3, 1,7,8,3, 1,7,9,3, + 1,8,0,3, 1,8,1,3, 1,8,2,3, 1,8,3,3, 1,8,4,3, 1,8,5,3, 1,8,6,3, 1,8,7,3, 1,8,8,3, + 1,8,9,3, 1,9,0,3, 1,9,1,3, 1,9,2,3, 1,9,3,3, 1,9,4,3, 1,9,5,3, 1,9,6,3, 1,9,7,3, + 1,9,8,3, 1,9,9,3, 2,0,0,3, 2,0,1,3, 2,0,2,3, 2,0,3,3, 2,0,4,3, 2,0,5,3, 2,0,6,3, + 2,0,7,3, 2,0,8,3, 2,0,9,3, 2,1,0,3, 2,1,1,3, 2,1,2,3, 2,1,3,3, 2,1,4,3, 2,1,5,3, + 2,1,6,3, 2,1,7,3, 2,1,8,3, 2,1,9,3, 2,2,0,3, 2,2,1,3, 2,2,2,3, 2,2,3,3, 2,2,4,3, + 2,2,5,3, 2,2,6,3, 2,2,7,3, 2,2,8,3, 2,2,9,3, 2,3,0,3, 2,3,1,3, 2,3,2,3, 2,3,3,3, + 2,3,4,3, 2,3,5,3, 2,3,6,3, 2,3,7,3, 2,3,8,3, 2,3,9,3, 2,4,0,3, 2,4,1,3, 2,4,2,3, + 2,4,3,3, 2,4,4,3, 2,4,5,3, 2,4,6,3, 2,4,7,3, 2,4,8,3, 2,4,9,3, 2,5,0,3, 2,5,1,3, + 2,5,2,3, 2,5,3,3, 2,5,4,3, 2,5,5,3, 2,5,6,3, 2,5,7,3, 2,5,8,3, 2,5,9,3, 2,6,0,3, + 2,6,1,3, 2,6,2,3, 2,6,3,3, 2,6,4,3, 2,6,5,3, 2,6,6,3, 2,6,7,3, 2,6,8,3, 2,6,9,3, + 2,7,0,3, 2,7,1,3, 2,7,2,3, 2,7,3,3, 2,7,4,3, 2,7,5,3, 2,7,6,3, 2,7,7,3, 2,7,8,3, + 2,7,9,3, 2,8,0,3, 2,8,1,3, 2,8,2,3, 2,8,3,3, 2,8,4,3, 2,8,5,3, 2,8,6,3, 2,8,7,3, + 2,8,8,3, 2,8,9,3, 2,9,0,3, 2,9,1,3, 2,9,2,3, 2,9,3,3, 2,9,4,3, 2,9,5,3, 2,9,6,3, + 2,9,7,3, 2,9,8,3, 2,9,9,3, 3,0,0,3, 3,0,1,3, 3,0,2,3, 3,0,3,3, 3,0,4,3, 3,0,5,3, + 3,0,6,3, 3,0,7,3, 3,0,8,3, 3,0,9,3, 3,1,0,3, 3,1,1,3, 3,1,2,3, 3,1,3,3, 3,1,4,3, + 3,1,5,3, 3,1,6,3, 3,1,7,3, 3,1,8,3, 3,1,9,3, 3,2,0,3, 3,2,1,3, 3,2,2,3, 3,2,3,3, + 3,2,4,3, 3,2,5,3, 3,2,6,3, 3,2,7,3, 3,2,8,3, 3,2,9,3, 3,3,0,3, 3,3,1,3, 3,3,2,3, + 3,3,3,3, 3,3,4,3, 3,3,5,3, 3,3,6,3, 3,3,7,3, 3,3,8,3, 3,3,9,3, 3,4,0,3, 3,4,1,3, + 3,4,2,3, 3,4,3,3, 3,4,4,3, 3,4,5,3, 3,4,6,3, 3,4,7,3, 3,4,8,3, 3,4,9,3, 3,5,0,3, + 3,5,1,3, 3,5,2,3, 3,5,3,3, 3,5,4,3, 3,5,5,3, 3,5,6,3, 3,5,7,3, 3,5,8,3, 3,5,9,3, + 3,6,0,3, 3,6,1,3, 3,6,2,3, 3,6,3,3, 3,6,4,3, 3,6,5,3, 3,6,6,3, 3,6,7,3, 3,6,8,3, + 3,6,9,3, 3,7,0,3, 3,7,1,3, 3,7,2,3, 3,7,3,3, 3,7,4,3, 3,7,5,3, 3,7,6,3, 3,7,7,3, + 3,7,8,3, 3,7,9,3, 3,8,0,3, 3,8,1,3, 3,8,2,3, 3,8,3,3, 3,8,4,3, 3,8,5,3, 3,8,6,3, + 3,8,7,3, 3,8,8,3, 3,8,9,3, 3,9,0,3, 3,9,1,3, 3,9,2,3, 3,9,3,3, 3,9,4,3, 3,9,5,3, + 3,9,6,3, 3,9,7,3, 3,9,8,3, 3,9,9,3, 4,0,0,3, 4,0,1,3, 4,0,2,3, 4,0,3,3, 4,0,4,3, + 4,0,5,3, 4,0,6,3, 4,0,7,3, 4,0,8,3, 4,0,9,3, 4,1,0,3, 4,1,1,3, 4,1,2,3, 4,1,3,3, + 4,1,4,3, 4,1,5,3, 4,1,6,3, 4,1,7,3, 4,1,8,3, 4,1,9,3, 4,2,0,3, 4,2,1,3, 4,2,2,3, + 4,2,3,3, 4,2,4,3, 4,2,5,3, 4,2,6,3, 4,2,7,3, 4,2,8,3, 4,2,9,3, 4,3,0,3, 4,3,1,3, + 4,3,2,3, 4,3,3,3, 4,3,4,3, 4,3,5,3, 4,3,6,3, 4,3,7,3, 4,3,8,3, 4,3,9,3, 4,4,0,3, + 4,4,1,3, 4,4,2,3, 4,4,3,3, 4,4,4,3, 4,4,5,3, 4,4,6,3, 4,4,7,3, 4,4,8,3, 4,4,9,3, + 4,5,0,3, 4,5,1,3, 4,5,2,3, 4,5,3,3, 4,5,4,3, 4,5,5,3, 4,5,6,3, 4,5,7,3, 4,5,8,3, + 4,5,9,3, 4,6,0,3, 4,6,1,3, 4,6,2,3, 4,6,3,3, 4,6,4,3, 4,6,5,3, 4,6,6,3, 4,6,7,3, + 4,6,8,3, 4,6,9,3, 4,7,0,3, 4,7,1,3, 4,7,2,3, 4,7,3,3, 4,7,4,3, 4,7,5,3, 4,7,6,3, + 4,7,7,3, 4,7,8,3, 4,7,9,3, 4,8,0,3, 4,8,1,3, 4,8,2,3, 4,8,3,3, 4,8,4,3, 4,8,5,3, + 4,8,6,3, 4,8,7,3, 4,8,8,3, 4,8,9,3, 4,9,0,3, 4,9,1,3, 4,9,2,3, 4,9,3,3, 4,9,4,3, + 4,9,5,3, 4,9,6,3, 4,9,7,3, 4,9,8,3, 4,9,9,3, 5,0,0,3, 5,0,1,3, 5,0,2,3, 5,0,3,3, + 5,0,4,3, 5,0,5,3, 5,0,6,3, 5,0,7,3, 5,0,8,3, 5,0,9,3, 5,1,0,3, 5,1,1,3, 5,1,2,3, + 5,1,3,3, 5,1,4,3, 5,1,5,3, 5,1,6,3, 5,1,7,3, 5,1,8,3, 5,1,9,3, 5,2,0,3, 5,2,1,3, + 5,2,2,3, 5,2,3,3, 5,2,4,3, 5,2,5,3, 5,2,6,3, 5,2,7,3, 5,2,8,3, 5,2,9,3, 5,3,0,3, + 5,3,1,3, 5,3,2,3, 5,3,3,3, 5,3,4,3, 5,3,5,3, 5,3,6,3, 5,3,7,3, 5,3,8,3, 5,3,9,3, + 5,4,0,3, 5,4,1,3, 5,4,2,3, 5,4,3,3, 5,4,4,3, 5,4,5,3, 5,4,6,3, 5,4,7,3, 5,4,8,3, + 5,4,9,3, 5,5,0,3, 5,5,1,3, 5,5,2,3, 5,5,3,3, 5,5,4,3, 5,5,5,3, 5,5,6,3, 5,5,7,3, + 5,5,8,3, 5,5,9,3, 5,6,0,3, 5,6,1,3, 5,6,2,3, 5,6,3,3, 5,6,4,3, 5,6,5,3, 5,6,6,3, + 5,6,7,3, 5,6,8,3, 5,6,9,3, 5,7,0,3, 5,7,1,3, 5,7,2,3, 5,7,3,3, 5,7,4,3, 5,7,5,3, + 5,7,6,3, 5,7,7,3, 5,7,8,3, 5,7,9,3, 5,8,0,3, 5,8,1,3, 5,8,2,3, 5,8,3,3, 5,8,4,3, + 5,8,5,3, 5,8,6,3, 5,8,7,3, 5,8,8,3, 5,8,9,3, 5,9,0,3, 5,9,1,3, 5,9,2,3, 5,9,3,3, + 5,9,4,3, 5,9,5,3, 5,9,6,3, 5,9,7,3, 5,9,8,3, 5,9,9,3, 6,0,0,3, 6,0,1,3, 6,0,2,3, + 6,0,3,3, 6,0,4,3, 6,0,5,3, 6,0,6,3, 6,0,7,3, 6,0,8,3, 6,0,9,3, 6,1,0,3, 6,1,1,3, + 6,1,2,3, 6,1,3,3, 6,1,4,3, 6,1,5,3, 6,1,6,3, 6,1,7,3, 6,1,8,3, 6,1,9,3, 6,2,0,3, + 6,2,1,3, 6,2,2,3, 6,2,3,3, 6,2,4,3, 6,2,5,3, 6,2,6,3, 6,2,7,3, 6,2,8,3, 6,2,9,3, + 6,3,0,3, 6,3,1,3, 6,3,2,3, 6,3,3,3, 6,3,4,3, 6,3,5,3, 6,3,6,3, 6,3,7,3, 6,3,8,3, + 6,3,9,3, 6,4,0,3, 6,4,1,3, 6,4,2,3, 6,4,3,3, 6,4,4,3, 6,4,5,3, 6,4,6,3, 6,4,7,3, + 6,4,8,3, 6,4,9,3, 6,5,0,3, 6,5,1,3, 6,5,2,3, 6,5,3,3, 6,5,4,3, 6,5,5,3, 6,5,6,3, + 6,5,7,3, 6,5,8,3, 6,5,9,3, 6,6,0,3, 6,6,1,3, 6,6,2,3, 6,6,3,3, 6,6,4,3, 6,6,5,3, + 6,6,6,3, 6,6,7,3, 6,6,8,3, 6,6,9,3, 6,7,0,3, 6,7,1,3, 6,7,2,3, 6,7,3,3, 6,7,4,3, + 6,7,5,3, 6,7,6,3, 6,7,7,3, 6,7,8,3, 6,7,9,3, 6,8,0,3, 6,8,1,3, 6,8,2,3, 6,8,3,3, + 6,8,4,3, 6,8,5,3, 6,8,6,3, 6,8,7,3, 6,8,8,3, 6,8,9,3, 6,9,0,3, 6,9,1,3, 6,9,2,3, + 6,9,3,3, 6,9,4,3, 6,9,5,3, 6,9,6,3, 6,9,7,3, 6,9,8,3, 6,9,9,3, 7,0,0,3, 7,0,1,3, + 7,0,2,3, 7,0,3,3, 7,0,4,3, 7,0,5,3, 7,0,6,3, 7,0,7,3, 7,0,8,3, 7,0,9,3, 7,1,0,3, + 7,1,1,3, 7,1,2,3, 7,1,3,3, 7,1,4,3, 7,1,5,3, 7,1,6,3, 7,1,7,3, 7,1,8,3, 7,1,9,3, + 7,2,0,3, 7,2,1,3, 7,2,2,3, 7,2,3,3, 7,2,4,3, 7,2,5,3, 7,2,6,3, 7,2,7,3, 7,2,8,3, + 7,2,9,3, 7,3,0,3, 7,3,1,3, 7,3,2,3, 7,3,3,3, 7,3,4,3, 7,3,5,3, 7,3,6,3, 7,3,7,3, + 7,3,8,3, 7,3,9,3, 7,4,0,3, 7,4,1,3, 7,4,2,3, 7,4,3,3, 7,4,4,3, 7,4,5,3, 7,4,6,3, + 7,4,7,3, 7,4,8,3, 7,4,9,3, 7,5,0,3, 7,5,1,3, 7,5,2,3, 7,5,3,3, 7,5,4,3, 7,5,5,3, + 7,5,6,3, 7,5,7,3, 7,5,8,3, 7,5,9,3, 7,6,0,3, 7,6,1,3, 7,6,2,3, 7,6,3,3, 7,6,4,3, + 7,6,5,3, 7,6,6,3, 7,6,7,3, 7,6,8,3, 7,6,9,3, 7,7,0,3, 7,7,1,3, 7,7,2,3, 7,7,3,3, + 7,7,4,3, 7,7,5,3, 7,7,6,3, 7,7,7,3, 7,7,8,3, 7,7,9,3, 7,8,0,3, 7,8,1,3, 7,8,2,3, + 7,8,3,3, 7,8,4,3, 7,8,5,3, 7,8,6,3, 7,8,7,3, 7,8,8,3, 7,8,9,3, 7,9,0,3, 7,9,1,3, + 7,9,2,3, 7,9,3,3, 7,9,4,3, 7,9,5,3, 7,9,6,3, 7,9,7,3, 7,9,8,3, 7,9,9,3, 8,0,0,3, + 8,0,1,3, 8,0,2,3, 8,0,3,3, 8,0,4,3, 8,0,5,3, 8,0,6,3, 8,0,7,3, 8,0,8,3, 8,0,9,3, + 8,1,0,3, 8,1,1,3, 8,1,2,3, 8,1,3,3, 8,1,4,3, 8,1,5,3, 8,1,6,3, 8,1,7,3, 8,1,8,3, + 8,1,9,3, 8,2,0,3, 8,2,1,3, 8,2,2,3, 8,2,3,3, 8,2,4,3, 8,2,5,3, 8,2,6,3, 8,2,7,3, + 8,2,8,3, 8,2,9,3, 8,3,0,3, 8,3,1,3, 8,3,2,3, 8,3,3,3, 8,3,4,3, 8,3,5,3, 8,3,6,3, + 8,3,7,3, 8,3,8,3, 8,3,9,3, 8,4,0,3, 8,4,1,3, 8,4,2,3, 8,4,3,3, 8,4,4,3, 8,4,5,3, + 8,4,6,3, 8,4,7,3, 8,4,8,3, 8,4,9,3, 8,5,0,3, 8,5,1,3, 8,5,2,3, 8,5,3,3, 8,5,4,3, + 8,5,5,3, 8,5,6,3, 8,5,7,3, 8,5,8,3, 8,5,9,3, 8,6,0,3, 8,6,1,3, 8,6,2,3, 8,6,3,3, + 8,6,4,3, 8,6,5,3, 8,6,6,3, 8,6,7,3, 8,6,8,3, 8,6,9,3, 8,7,0,3, 8,7,1,3, 8,7,2,3, + 8,7,3,3, 8,7,4,3, 8,7,5,3, 8,7,6,3, 8,7,7,3, 8,7,8,3, 8,7,9,3, 8,8,0,3, 8,8,1,3, + 8,8,2,3, 8,8,3,3, 8,8,4,3, 8,8,5,3, 8,8,6,3, 8,8,7,3, 8,8,8,3, 8,8,9,3, 8,9,0,3, + 8,9,1,3, 8,9,2,3, 8,9,3,3, 8,9,4,3, 8,9,5,3, 8,9,6,3, 8,9,7,3, 8,9,8,3, 8,9,9,3, + 9,0,0,3, 9,0,1,3, 9,0,2,3, 9,0,3,3, 9,0,4,3, 9,0,5,3, 9,0,6,3, 9,0,7,3, 9,0,8,3, + 9,0,9,3, 9,1,0,3, 9,1,1,3, 9,1,2,3, 9,1,3,3, 9,1,4,3, 9,1,5,3, 9,1,6,3, 9,1,7,3, + 9,1,8,3, 9,1,9,3, 9,2,0,3, 9,2,1,3, 9,2,2,3, 9,2,3,3, 9,2,4,3, 9,2,5,3, 9,2,6,3, + 9,2,7,3, 9,2,8,3, 9,2,9,3, 9,3,0,3, 9,3,1,3, 9,3,2,3, 9,3,3,3, 9,3,4,3, 9,3,5,3, + 9,3,6,3, 9,3,7,3, 9,3,8,3, 9,3,9,3, 9,4,0,3, 9,4,1,3, 9,4,2,3, 9,4,3,3, 9,4,4,3, + 9,4,5,3, 9,4,6,3, 9,4,7,3, 9,4,8,3, 9,4,9,3, 9,5,0,3, 9,5,1,3, 9,5,2,3, 9,5,3,3, + 9,5,4,3, 9,5,5,3, 9,5,6,3, 9,5,7,3, 9,5,8,3, 9,5,9,3, 9,6,0,3, 9,6,1,3, 9,6,2,3, + 9,6,3,3, 9,6,4,3, 9,6,5,3, 9,6,6,3, 9,6,7,3, 9,6,8,3, 9,6,9,3, 9,7,0,3, 9,7,1,3, + 9,7,2,3, 9,7,3,3, 9,7,4,3, 9,7,5,3, 9,7,6,3, 9,7,7,3, 9,7,8,3, 9,7,9,3, 9,8,0,3, + 9,8,1,3, 9,8,2,3, 9,8,3,3, 9,8,4,3, 9,8,5,3, 9,8,6,3, 9,8,7,3, 9,8,8,3, 9,8,9,3, + 9,9,0,3, 9,9,1,3, 9,9,2,3, 9,9,3,3, 9,9,4,3, 9,9,5,3, 9,9,6,3, 9,9,7,3, 9,9,8,3, + 9,9,9,3}; +#endif + diff --git a/extern/decNumber/decDouble.c b/extern/decNumber/decDouble.c new file mode 100644 index 0000000000..e63ef23fab --- /dev/null +++ b/extern/decNumber/decDouble.c @@ -0,0 +1,140 @@ +/* ------------------------------------------------------------------ */ +/* decDouble.c -- decDouble operations module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises decDouble operations (including conversions) */ +/* ------------------------------------------------------------------ */ + +#include "decContext.h" // public includes +#include "decDouble.h" // .. + +/* Constant mappings for shared code */ +#define DECPMAX DECDOUBLE_Pmax +#define DECEMIN DECDOUBLE_Emin +#define DECEMAX DECDOUBLE_Emax +#define DECEMAXD DECDOUBLE_EmaxD +#define DECBYTES DECDOUBLE_Bytes +#define DECSTRING DECDOUBLE_String +#define DECECONL DECDOUBLE_EconL +#define DECBIAS DECDOUBLE_Bias +#define DECLETS DECDOUBLE_Declets +#define DECQTINY (-DECDOUBLE_Bias) +// parameters of next-wider format +#define DECWBYTES DECQUAD_Bytes +#define DECWPMAX DECQUAD_Pmax +#define DECWECONL DECQUAD_EconL +#define DECWBIAS DECQUAD_Bias + +/* Type and function mappings for shared code */ +#define decFloat decDouble // Type name +#define decFloatWider decQuad // Type name + +// Utilities and conversions (binary results, extractors, etc.) +#define decFloatFromBCD decDoubleFromBCD +#define decFloatFromInt32 decDoubleFromInt32 +#define decFloatFromPacked decDoubleFromPacked +#define decFloatFromPackedChecked decDoubleFromPackedChecked +#define decFloatFromString decDoubleFromString +#define decFloatFromUInt32 decDoubleFromUInt32 +#define decFloatFromWider decDoubleFromWider +#define decFloatGetCoefficient decDoubleGetCoefficient +#define decFloatGetExponent decDoubleGetExponent +#define decFloatSetCoefficient decDoubleSetCoefficient +#define decFloatSetExponent decDoubleSetExponent +#define decFloatShow decDoubleShow +#define decFloatToBCD decDoubleToBCD +#define decFloatToEngString decDoubleToEngString +#define decFloatToInt32 decDoubleToInt32 +#define decFloatToInt32Exact decDoubleToInt32Exact +#define decFloatToPacked decDoubleToPacked +#define decFloatToString decDoubleToString +#define decFloatToUInt32 decDoubleToUInt32 +#define decFloatToUInt32Exact decDoubleToUInt32Exact +#define decFloatToWider decDoubleToWider +#define decFloatZero decDoubleZero + +// Computational (result is a decFloat) +#define decFloatAbs decDoubleAbs +#define decFloatAdd decDoubleAdd +#define decFloatAnd decDoubleAnd +#define decFloatDivide decDoubleDivide +#define decFloatDivideInteger decDoubleDivideInteger +#define decFloatFMA decDoubleFMA +#define decFloatInvert decDoubleInvert +#define decFloatLogB decDoubleLogB +#define decFloatMax decDoubleMax +#define decFloatMaxMag decDoubleMaxMag +#define decFloatMin decDoubleMin +#define decFloatMinMag decDoubleMinMag +#define decFloatMinus decDoubleMinus +#define decFloatMultiply decDoubleMultiply +#define decFloatNextMinus decDoubleNextMinus +#define decFloatNextPlus decDoubleNextPlus +#define decFloatNextToward decDoubleNextToward +#define decFloatOr decDoubleOr +#define decFloatPlus decDoublePlus +#define decFloatQuantize decDoubleQuantize +#define decFloatReduce decDoubleReduce +#define decFloatRemainder decDoubleRemainder +#define decFloatRemainderNear decDoubleRemainderNear +#define decFloatRotate decDoubleRotate +#define decFloatScaleB decDoubleScaleB +#define decFloatShift decDoubleShift +#define decFloatSubtract decDoubleSubtract +#define decFloatToIntegralValue decDoubleToIntegralValue +#define decFloatToIntegralExact decDoubleToIntegralExact +#define decFloatXor decDoubleXor + +// Comparisons +#define decFloatCompare decDoubleCompare +#define decFloatCompareSignal decDoubleCompareSignal +#define decFloatCompareTotal decDoubleCompareTotal +#define decFloatCompareTotalMag decDoubleCompareTotalMag + +// Copies +#define decFloatCanonical decDoubleCanonical +#define decFloatCopy decDoubleCopy +#define decFloatCopyAbs decDoubleCopyAbs +#define decFloatCopyNegate decDoubleCopyNegate +#define decFloatCopySign decDoubleCopySign + +// Non-computational +#define decFloatClass decDoubleClass +#define decFloatClassString decDoubleClassString +#define decFloatDigits decDoubleDigits +#define decFloatIsCanonical decDoubleIsCanonical +#define decFloatIsFinite decDoubleIsFinite +#define decFloatIsInfinite decDoubleIsInfinite +#define decFloatIsInteger decDoubleIsInteger +#define decFloatIsLogical decDoubleIsLogical +#define decFloatIsNaN decDoubleIsNaN +#define decFloatIsNegative decDoubleIsNegative +#define decFloatIsNormal decDoubleIsNormal +#define decFloatIsPositive decDoubleIsPositive +#define decFloatIsSignaling decDoubleIsSignaling +#define decFloatIsSignalling decDoubleIsSignalling +#define decFloatIsSigned decDoubleIsSigned +#define decFloatIsSubnormal decDoubleIsSubnormal +#define decFloatIsZero decDoubleIsZero +#define decFloatRadix decDoubleRadix +#define decFloatSameQuantum decDoubleSameQuantum +#define decFloatVersion decDoubleVersion + +#include "decNumberLocal.h" // local includes (need DECPMAX) +#include "decCommon.c" // non-arithmetic decFloat routines +#include "decBasic.c" // basic formats routines + diff --git a/extern/decNumber/decDouble.h b/extern/decNumber/decDouble.h new file mode 100644 index 0000000000..2e789f6361 --- /dev/null +++ b/extern/decNumber/decDouble.h @@ -0,0 +1,155 @@ +/* ------------------------------------------------------------------ */ +/* decDouble.h -- Decimal 64-bit format module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECDOUBLE) + #define DECDOUBLE + + #define DECDOUBLENAME "decimalDouble" /* Short name */ + #define DECDOUBLETITLE "Decimal 64-bit datum" /* Verbose name */ + #define DECDOUBLEAUTHOR "Mike Cowlishaw" /* Who to blame */ + + /* parameters for decDoubles */ + #define DECDOUBLE_Bytes 8 /* length */ + #define DECDOUBLE_Pmax 16 /* maximum precision (digits) */ + #define DECDOUBLE_Emin -383 /* minimum adjusted exponent */ + #define DECDOUBLE_Emax 384 /* maximum adjusted exponent */ + #define DECDOUBLE_EmaxD 3 /* maximum exponent digits */ + #define DECDOUBLE_Bias 398 /* bias for the exponent */ + #define DECDOUBLE_String 25 /* maximum string length, +1 */ + #define DECDOUBLE_EconL 8 /* exponent continuation length */ + #define DECDOUBLE_Declets 5 /* count of declets */ + /* highest biased exponent (Elimit-1) */ + #define DECDOUBLE_Ehigh (DECDOUBLE_Emax + DECDOUBLE_Bias - (DECDOUBLE_Pmax-1)) + + /* Required includes */ + #include "decContext.h" + #include "decQuad.h" + + /* The decDouble decimal 64-bit type, accessible by all sizes */ + typedef union { + uint8_t bytes[DECDOUBLE_Bytes]; /* fields: 1, 5, 8, 50 bits */ + uint16_t shorts[DECDOUBLE_Bytes/2]; + uint32_t words[DECDOUBLE_Bytes/4]; + #if DECUSE64 + uint64_t longs[DECDOUBLE_Bytes/8]; + #endif + } decDouble; + + /* ---------------------------------------------------------------- */ + /* Routines -- implemented as decFloat routines in common files */ + /* ---------------------------------------------------------------- */ + + /* Utilities and conversions, extractors, etc.) */ + extern decDouble * decDoubleFromBCD(decDouble *, int32_t, const uint8_t *, int32_t); + extern decDouble * decDoubleFromInt32(decDouble *, int32_t); + extern decDouble * decDoubleFromPacked(decDouble *, int32_t, const uint8_t *); + extern decDouble * decDoubleFromPackedChecked(decDouble *, int32_t, const uint8_t *); + extern decDouble * decDoubleFromString(decDouble *, const char *, decContext *); + extern decDouble * decDoubleFromUInt32(decDouble *, uint32_t); + extern decDouble * decDoubleFromWider(decDouble *, const decQuad *, decContext *); + extern int32_t decDoubleGetCoefficient(const decDouble *, uint8_t *); + extern int32_t decDoubleGetExponent(const decDouble *); + extern decDouble * decDoubleSetCoefficient(decDouble *, const uint8_t *, int32_t); + extern decDouble * decDoubleSetExponent(decDouble *, decContext *, int32_t); + extern void decDoubleShow(const decDouble *, const char *); + extern int32_t decDoubleToBCD(const decDouble *, int32_t *, uint8_t *); + extern char * decDoubleToEngString(const decDouble *, char *); + extern int32_t decDoubleToInt32(const decDouble *, decContext *, enum rounding); + extern int32_t decDoubleToInt32Exact(const decDouble *, decContext *, enum rounding); + extern int32_t decDoubleToPacked(const decDouble *, int32_t *, uint8_t *); + extern char * decDoubleToString(const decDouble *, char *); + extern uint32_t decDoubleToUInt32(const decDouble *, decContext *, enum rounding); + extern uint32_t decDoubleToUInt32Exact(const decDouble *, decContext *, enum rounding); + extern decQuad * decDoubleToWider(const decDouble *, decQuad *); + extern decDouble * decDoubleZero(decDouble *); + + /* Computational (result is a decDouble) */ + extern decDouble * decDoubleAbs(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleAdd(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleAnd(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleDivide(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleDivideInteger(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleFMA(decDouble *, const decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleInvert(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleLogB(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleMax(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleMaxMag(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleMin(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleMinMag(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleMinus(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleMultiply(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleNextMinus(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleNextPlus(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleNextToward(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleOr(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoublePlus(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleQuantize(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleReduce(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleRemainder(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleRemainderNear(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleRotate(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleScaleB(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleShift(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleSubtract(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleToIntegralValue(decDouble *, const decDouble *, decContext *, enum rounding); + extern decDouble * decDoubleToIntegralExact(decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleXor(decDouble *, const decDouble *, const decDouble *, decContext *); + + /* Comparisons */ + extern decDouble * decDoubleCompare(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleCompareSignal(decDouble *, const decDouble *, const decDouble *, decContext *); + extern decDouble * decDoubleCompareTotal(decDouble *, const decDouble *, const decDouble *); + extern decDouble * decDoubleCompareTotalMag(decDouble *, const decDouble *, const decDouble *); + + /* Copies */ + extern decDouble * decDoubleCanonical(decDouble *, const decDouble *); + extern decDouble * decDoubleCopy(decDouble *, const decDouble *); + extern decDouble * decDoubleCopyAbs(decDouble *, const decDouble *); + extern decDouble * decDoubleCopyNegate(decDouble *, const decDouble *); + extern decDouble * decDoubleCopySign(decDouble *, const decDouble *, const decDouble *); + + /* Non-computational */ + extern enum decClass decDoubleClass(const decDouble *); + extern const char * decDoubleClassString(const decDouble *); + extern uint32_t decDoubleDigits(const decDouble *); + extern uint32_t decDoubleIsCanonical(const decDouble *); + extern uint32_t decDoubleIsFinite(const decDouble *); + extern uint32_t decDoubleIsInfinite(const decDouble *); + extern uint32_t decDoubleIsInteger(const decDouble *); + extern uint32_t decDoubleIsLogical(const decDouble *); + extern uint32_t decDoubleIsNaN(const decDouble *); + extern uint32_t decDoubleIsNegative(const decDouble *); + extern uint32_t decDoubleIsNormal(const decDouble *); + extern uint32_t decDoubleIsPositive(const decDouble *); + extern uint32_t decDoubleIsSignaling(const decDouble *); + extern uint32_t decDoubleIsSignalling(const decDouble *); + extern uint32_t decDoubleIsSigned(const decDouble *); + extern uint32_t decDoubleIsSubnormal(const decDouble *); + extern uint32_t decDoubleIsZero(const decDouble *); + extern uint32_t decDoubleRadix(const decDouble *); + extern uint32_t decDoubleSameQuantum(const decDouble *, const decDouble *); + extern const char * decDoubleVersion(void); + + /* decNumber conversions; these are implemented as macros so as not */ + /* to force a dependency on decimal64 and decNumber in decDouble. */ + /* decDoubleFromNumber returns a decimal64 * to avoid warnings. */ + #define decDoubleToNumber(dq, dn) decimal64ToNumber((decimal64 *)(dq), dn) + #define decDoubleFromNumber(dq, dn, set) decimal64FromNumber((decimal64 *)(dq), dn, set) + +#endif diff --git a/extern/decNumber/decNumber.c b/extern/decNumber/decNumber.c new file mode 100644 index 0000000000..2572fac5b1 --- /dev/null +++ b/extern/decNumber/decNumber.c @@ -0,0 +1,8141 @@ +/* ------------------------------------------------------------------ */ +/* Decimal Number arithmetic module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2009. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for arbitrary-precision General */ +/* Decimal Arithmetic as defined in the specification which may be */ +/* found on the General Decimal Arithmetic pages. It implements both */ +/* the full ('extended') arithmetic and the simpler ('subset') */ +/* arithmetic. */ +/* */ +/* Usage notes: */ +/* */ +/* 1. This code is ANSI C89 except: */ +/* */ +/* a) C99 line comments (double forward slash) are used. (Most C */ +/* compilers accept these. If yours does not, a simple script */ +/* can be used to convert them to ANSI C comments.) */ +/* */ +/* b) Types from C99 stdint.h are used. If you do not have this */ +/* header file, see the User's Guide section of the decNumber */ +/* documentation; this lists the necessary definitions. */ +/* */ +/* c) If DECDPUN>4 or DECUSE64=1, the C99 64-bit int64_t and */ +/* uint64_t types may be used. To avoid these, set DECUSE64=0 */ +/* and DECDPUN<=4 (see documentation). */ +/* */ +/* The code also conforms to C99 restrictions; in particular, */ +/* strict aliasing rules are observed. */ +/* */ +/* 2. The decNumber format which this library uses is optimized for */ +/* efficient processing of relatively short numbers; in particular */ +/* it allows the use of fixed sized structures and minimizes copy */ +/* and move operations. It does, however, support arbitrary */ +/* precision (up to 999,999,999 digits) and arbitrary exponent */ +/* range (Emax in the range 0 through 999,999,999 and Emin in the */ +/* range -999,999,999 through 0). Mathematical functions (for */ +/* example decNumberExp) as identified below are restricted more */ +/* tightly: digits, emax, and -emin in the context must be <= */ +/* DEC_MAX_MATH (999999), and their operand(s) must be within */ +/* these bounds. */ +/* */ +/* 3. Logical functions are further restricted; their operands must */ +/* be finite, positive, have an exponent of zero, and all digits */ +/* must be either 0 or 1. The result will only contain digits */ +/* which are 0 or 1 (and will have exponent=0 and a sign of 0). */ +/* */ +/* 4. Operands to operator functions are never modified unless they */ +/* are also specified to be the result number (which is always */ +/* permitted). Other than that case, operands must not overlap. */ +/* */ +/* 5. Error handling: the type of the error is ORed into the status */ +/* flags in the current context (decContext structure). The */ +/* SIGFPE signal is then raised if the corresponding trap-enabler */ +/* flag in the decContext is set (is 1). */ +/* */ +/* It is the responsibility of the caller to clear the status */ +/* flags as required. */ +/* */ +/* The result of any routine which returns a number will always */ +/* be a valid number (which may be a special value, such as an */ +/* Infinity or NaN). */ +/* */ +/* 6. The decNumber format is not an exchangeable concrete */ +/* representation as it comprises fields which may be machine- */ +/* dependent (packed or unpacked, or special length, for example). */ +/* Canonical conversions to and from strings are provided; other */ +/* conversions are available in separate modules. */ +/* */ +/* 7. Normally, input operands are assumed to be valid. Set DECCHECK */ +/* to 1 for extended operand checking (including NULL operands). */ +/* Results are undefined if a badly-formed structure (or a NULL */ +/* pointer to a structure) is provided, though with DECCHECK */ +/* enabled the operator routines are protected against exceptions. */ +/* (Except if the result pointer is NULL, which is unrecoverable.) */ +/* */ +/* However, the routines will never cause exceptions if they are */ +/* given well-formed operands, even if the value of the operands */ +/* is inappropriate for the operation and DECCHECK is not set. */ +/* (Except for SIGFPE, as and where documented.) */ +/* */ +/* 8. Subset arithmetic is available only if DECSUBSET is set to 1. */ +/* ------------------------------------------------------------------ */ +/* Implementation notes for maintenance of this module: */ +/* */ +/* 1. Storage leak protection: Routines which use malloc are not */ +/* permitted to use return for fastpath or error exits (i.e., */ +/* they follow strict structured programming conventions). */ +/* Instead they have a do{}while(0); construct surrounding the */ +/* code which is protected -- break may be used to exit this. */ +/* Other routines can safely use the return statement inline. */ +/* */ +/* Storage leak accounting can be enabled using DECALLOC. */ +/* */ +/* 2. All loops use the for(;;) construct. Any do construct does */ +/* not loop; it is for allocation protection as just described. */ +/* */ +/* 3. Setting status in the context must always be the very last */ +/* action in a routine, as non-0 status may raise a trap and hence */ +/* the call to set status may not return (if the handler uses long */ +/* jump). Therefore all cleanup must be done first. In general, */ +/* to achieve this status is accumulated and is only applied just */ +/* before return by calling decContextSetStatus (via decStatus). */ +/* */ +/* Routines which allocate storage cannot, in general, use the */ +/* 'top level' routines which could cause a non-returning */ +/* transfer of control. The decXxxxOp routines are safe (do not */ +/* call decStatus even if traps are set in the context) and should */ +/* be used instead (they are also a little faster). */ +/* */ +/* 4. Exponent checking is minimized by allowing the exponent to */ +/* grow outside its limits during calculations, provided that */ +/* the decFinalize function is called later. Multiplication and */ +/* division, and intermediate calculations in exponentiation, */ +/* require more careful checks because of the risk of 31-bit */ +/* overflow (the most negative valid exponent is -1999999997, for */ +/* a 999999999-digit number with adjusted exponent of -999999999). */ +/* */ +/* 5. Rounding is deferred until finalization of results, with any */ +/* 'off to the right' data being represented as a single digit */ +/* residue (in the range -1 through 9). This avoids any double- */ +/* rounding when more than one shortening takes place (for */ +/* example, when a result is subnormal). */ +/* */ +/* 6. The digits count is allowed to rise to a multiple of DECDPUN */ +/* during many operations, so whole Units are handled and exact */ +/* accounting of digits is not needed. The correct digits value */ +/* is found by decGetDigits, which accounts for leading zeros. */ +/* This must be called before any rounding if the number of digits */ +/* is not known exactly. */ +/* */ +/* 7. The multiply-by-reciprocal 'trick' is used for partitioning */ +/* numbers up to four digits, using appropriate constants. This */ +/* is not useful for longer numbers because overflow of 32 bits */ +/* would lead to 4 multiplies, which is almost as expensive as */ +/* a divide (unless a floating-point or 64-bit multiply is */ +/* assumed to be available). */ +/* */ +/* 8. Unusual abbreviations that may be used in the commentary: */ +/* lhs -- left hand side (operand, of an operation) */ +/* lsd -- least significant digit (of coefficient) */ +/* lsu -- least significant Unit (of coefficient) */ +/* msd -- most significant digit (of coefficient) */ +/* msi -- most significant item (in an array) */ +/* msu -- most significant Unit (of coefficient) */ +/* rhs -- right hand side (operand, of an operation) */ +/* +ve -- positive */ +/* -ve -- negative */ +/* ** -- raise to the power */ +/* ------------------------------------------------------------------ */ + +#include // for malloc, free, etc. +#include // for printf [if needed] +#include // for strcpy +#include // for lower +#include "decNumber.h" // base number library +#include "decNumberLocal.h" // decNumber local types, etc. + +/* Constants */ +// Public lookup table used by the D2U macro +const uByte d2utable[DECMAXD2U+1]=D2UTABLE; + +#define DECVERB 1 // set to 1 for verbose DECCHECK +#define powers DECPOWERS // old internal name + +// Local constants +#define DIVIDE 0x80 // Divide operators +#define REMAINDER 0x40 // .. +#define DIVIDEINT 0x20 // .. +#define REMNEAR 0x10 // .. +#define COMPARE 0x01 // Compare operators +#define COMPMAX 0x02 // .. +#define COMPMIN 0x03 // .. +#define COMPTOTAL 0x04 // .. +#define COMPNAN 0x05 // .. [NaN processing] +#define COMPSIG 0x06 // .. [signaling COMPARE] +#define COMPMAXMAG 0x07 // .. +#define COMPMINMAG 0x08 // .. + +#define DEC_sNaN 0x40000000 // local status: sNaN signal +#define BADINT (Int)0x80000000 // most-negative Int; error indicator +// Next two indicate an integer >= 10**6, and its parity (bottom bit) +#define BIGEVEN (Int)0x80000002 +#define BIGODD (Int)0x80000003 + +static Unit uarrone[1]={1}; // Unit array of 1, used for incrementing + +/* Granularity-dependent code */ +#if DECDPUN<=4 + #define eInt Int // extended integer + #define ueInt uInt // unsigned extended integer + // Constant multipliers for divide-by-power-of five using reciprocal + // multiply, after removing powers of 2 by shifting, and final shift + // of 17 [we only need up to **4] + static const uInt multies[]={131073, 26215, 5243, 1049, 210}; + // QUOT10 -- macro to return the quotient of unit u divided by 10**n + #define QUOT10(u, n) ((((uInt)(u)>>(n))*multies[n])>>17) +#else + // For DECDPUN>4 non-ANSI-89 64-bit types are needed. + #if !DECUSE64 + #error decNumber.c: DECUSE64 must be 1 when DECDPUN>4 + #endif + #define eInt Long // extended integer + #define ueInt uLong // unsigned extended integer +#endif + +/* Local routines */ +static decNumber * decAddOp(decNumber *, const decNumber *, const decNumber *, + decContext *, uByte, uInt *); +static Flag decBiStr(const char *, const char *, const char *); +static uInt decCheckMath(const decNumber *, decContext *, uInt *); +static void decApplyRound(decNumber *, decContext *, Int, uInt *); +static Int decCompare(const decNumber *lhs, const decNumber *rhs, Flag); +static decNumber * decCompareOp(decNumber *, const decNumber *, + const decNumber *, decContext *, + Flag, uInt *); +static void decCopyFit(decNumber *, const decNumber *, decContext *, + Int *, uInt *); +static decNumber * decDecap(decNumber *, Int); +static decNumber * decDivideOp(decNumber *, const decNumber *, + const decNumber *, decContext *, Flag, uInt *); +static decNumber * decExpOp(decNumber *, const decNumber *, + decContext *, uInt *); +static void decFinalize(decNumber *, decContext *, Int *, uInt *); +static Int decGetDigits(Unit *, Int); +static Int decGetInt(const decNumber *); +static decNumber * decLnOp(decNumber *, const decNumber *, + decContext *, uInt *); +static decNumber * decMultiplyOp(decNumber *, const decNumber *, + const decNumber *, decContext *, + uInt *); +static decNumber * decNaNs(decNumber *, const decNumber *, + const decNumber *, decContext *, uInt *); +static decNumber * decQuantizeOp(decNumber *, const decNumber *, + const decNumber *, decContext *, Flag, + uInt *); +static void decReverse(Unit *, Unit *); +static void decSetCoeff(decNumber *, decContext *, const Unit *, + Int, Int *, uInt *); +static void decSetMaxValue(decNumber *, decContext *); +static void decSetOverflow(decNumber *, decContext *, uInt *); +static void decSetSubnormal(decNumber *, decContext *, Int *, uInt *); +static Int decShiftToLeast(Unit *, Int, Int); +static Int decShiftToMost(Unit *, Int, Int); +static void decStatus(decNumber *, uInt, decContext *); +static void decToString(const decNumber *, char[], Flag); +static decNumber * decTrim(decNumber *, decContext *, Flag, Flag, Int *); +static Int decUnitAddSub(const Unit *, Int, const Unit *, Int, Int, + Unit *, Int); +static Int decUnitCompare(const Unit *, Int, const Unit *, Int, Int); + +#if !DECSUBSET +/* decFinish == decFinalize when no subset arithmetic needed */ +#define decFinish(a,b,c,d) decFinalize(a,b,c,d) +#else +static void decFinish(decNumber *, decContext *, Int *, uInt *); +static decNumber * decRoundOperand(const decNumber *, decContext *, uInt *); +#endif + +/* Local macros */ +// masked special-values bits +#define SPECIALARG (rhs->bits & DECSPECIAL) +#define SPECIALARGS ((lhs->bits | rhs->bits) & DECSPECIAL) + +/* Diagnostic macros, etc. */ +#if DECALLOC +// Handle malloc/free accounting. If enabled, our accountable routines +// are used; otherwise the code just goes straight to the system malloc +// and free routines. +#define malloc(a) decMalloc(a) +#define free(a) decFree(a) +#define DECFENCE 0x5a // corruption detector +// 'Our' malloc and free: +static void *decMalloc(size_t); +static void decFree(void *); +uInt decAllocBytes=0; // count of bytes allocated +// Note that DECALLOC code only checks for storage buffer overflow. +// To check for memory leaks, the decAllocBytes variable must be +// checked to be 0 at appropriate times (e.g., after the test +// harness completes a set of tests). This checking may be unreliable +// if the testing is done in a multi-thread environment. +#endif + +#if DECCHECK +// Optional checking routines. Enabling these means that decNumber +// and decContext operands to operator routines are checked for +// correctness. This roughly doubles the execution time of the +// fastest routines (and adds 600+ bytes), so should not normally be +// used in 'production'. +// decCheckInexact is used to check that inexact results have a full +// complement of digits (where appropriate -- this is not the case +// for Quantize, for example) +#define DECUNRESU ((decNumber *)(void *)0xffffffff) +#define DECUNUSED ((const decNumber *)(void *)0xffffffff) +#define DECUNCONT ((decContext *)(void *)(0xffffffff)) +static Flag decCheckOperands(decNumber *, const decNumber *, + const decNumber *, decContext *); +static Flag decCheckNumber(const decNumber *); +static void decCheckInexact(const decNumber *, decContext *); +#endif + +#if DECTRACE || DECCHECK +// Optional trace/debugging routines (may or may not be used) +void decNumberShow(const decNumber *); // displays the components of a number +static void decDumpAr(char, const Unit *, Int); +#endif + +/* ================================================================== */ +/* Conversions */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* from-int32 -- conversion from Int or uInt */ +/* */ +/* dn is the decNumber to receive the integer */ +/* in or uin is the integer to be converted */ +/* returns dn */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberFromInt32(decNumber *dn, Int in) { + uInt unsig; + if (in>=0) unsig=in; + else { // negative (possibly BADINT) + if (in==BADINT) unsig=(uInt)1073741824*2; // special case + else unsig=-in; // invert + } + // in is now positive + decNumberFromUInt32(dn, unsig); + if (in<0) dn->bits=DECNEG; // sign needed + return dn; + } // decNumberFromInt32 + +decNumber * decNumberFromUInt32(decNumber *dn, uInt uin) { + Unit *up; // work pointer + decNumberZero(dn); // clean + if (uin==0) return dn; // [or decGetDigits bad call] + for (up=dn->lsu; uin>0; up++) { + *up=(Unit)(uin%(DECDPUNMAX+1)); + uin=uin/(DECDPUNMAX+1); + } + dn->digits=decGetDigits(dn->lsu, up-dn->lsu); + return dn; + } // decNumberFromUInt32 + +/* ------------------------------------------------------------------ */ +/* to-int32 -- conversion to Int or uInt */ +/* */ +/* dn is the decNumber to convert */ +/* set is the context for reporting errors */ +/* returns the converted decNumber, or 0 if Invalid is set */ +/* */ +/* Invalid is set if the decNumber does not have exponent==0 or if */ +/* it is a NaN, Infinite, or out-of-range. */ +/* ------------------------------------------------------------------ */ +Int decNumberToInt32(const decNumber *dn, decContext *set) { + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; + #endif + + // special or too many digits, or bad exponent + if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0) ; // bad + else { // is a finite integer with 10 or fewer digits + Int d; // work + const Unit *up; // .. + uInt hi=0, lo; // .. + up=dn->lsu; // -> lsu + lo=*up; // get 1 to 9 digits + #if DECDPUN>1 // split to higher + hi=lo/10; + lo=lo%10; + #endif + up++; + // collect remaining Units, if any, into hi + for (d=DECDPUN; ddigits; up++, d+=DECDPUN) hi+=*up*powers[d-1]; + // now low has the lsd, hi the remainder + if (hi>214748364 || (hi==214748364 && lo>7)) { // out of range? + // most-negative is a reprieve + if (dn->bits&DECNEG && hi==214748364 && lo==8) return 0x80000000; + // bad -- drop through + } + else { // in-range always + Int i=X10(hi)+lo; + if (dn->bits&DECNEG) return -i; + return i; + } + } // integer + decContextSetStatus(set, DEC_Invalid_operation); // [may not return] + return 0; + } // decNumberToInt32 + +uInt decNumberToUInt32(const decNumber *dn, decContext *set) { + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; + #endif + // special or too many digits, or bad exponent, or negative (<0) + if (dn->bits&DECSPECIAL || dn->digits>10 || dn->exponent!=0 + || (dn->bits&DECNEG && !ISZERO(dn))); // bad + else { // is a finite integer with 10 or fewer digits + Int d; // work + const Unit *up; // .. + uInt hi=0, lo; // .. + up=dn->lsu; // -> lsu + lo=*up; // get 1 to 9 digits + #if DECDPUN>1 // split to higher + hi=lo/10; + lo=lo%10; + #endif + up++; + // collect remaining Units, if any, into hi + for (d=DECDPUN; ddigits; up++, d+=DECDPUN) hi+=*up*powers[d-1]; + + // now low has the lsd, hi the remainder + if (hi>429496729 || (hi==429496729 && lo>5)) ; // no reprieve possible + else return X10(hi)+lo; + } // integer + decContextSetStatus(set, DEC_Invalid_operation); // [may not return] + return 0; + } // decNumberToUInt32 + +/* ------------------------------------------------------------------ */ +/* to-scientific-string -- conversion to numeric string */ +/* to-engineering-string -- conversion to numeric string */ +/* */ +/* decNumberToString(dn, string); */ +/* decNumberToEngString(dn, string); */ +/* */ +/* dn is the decNumber to convert */ +/* string is the string where the result will be laid out */ +/* */ +/* string must be at least dn->digits+14 characters long */ +/* */ +/* No error is possible, and no status can be set. */ +/* ------------------------------------------------------------------ */ +char * decNumberToString(const decNumber *dn, char *string){ + decToString(dn, string, 0); + return string; + } // DecNumberToString + +char * decNumberToEngString(const decNumber *dn, char *string){ + decToString(dn, string, 1); + return string; + } // DecNumberToEngString + +/* ------------------------------------------------------------------ */ +/* to-number -- conversion from numeric string */ +/* */ +/* decNumberFromString -- convert string to decNumber */ +/* dn -- the number structure to fill */ +/* chars[] -- the string to convert ('\0' terminated) */ +/* set -- the context used for processing any error, */ +/* determining the maximum precision available */ +/* (set.digits), determining the maximum and minimum */ +/* exponent (set.emax and set.emin), determining if */ +/* extended values are allowed, and checking the */ +/* rounding mode if overflow occurs or rounding is */ +/* needed. */ +/* */ +/* The length of the coefficient and the size of the exponent are */ +/* checked by this routine, so the correct error (Underflow or */ +/* Overflow) can be reported or rounding applied, as necessary. */ +/* */ +/* If bad syntax is detected, the result will be a quiet NaN. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberFromString(decNumber *dn, const char chars[], + decContext *set) { + Int exponent=0; // working exponent [assume 0] + uByte bits=0; // working flags [assume +ve] + Unit *res; // where result will be built + Unit resbuff[SD2U(DECBUFFER+9)];// local buffer in case need temporary + // [+9 allows for ln() constants] + Unit *allocres=NULL; // -> allocated result, iff allocated + Int d=0; // count of digits found in decimal part + const char *dotchar=NULL; // where dot was found + const char *cfirst=chars; // -> first character of decimal part + const char *last=NULL; // -> last digit of decimal part + const char *c; // work + Unit *up; // .. + #if DECDPUN>1 + Int cut, out; // .. + #endif + Int residue; // rounding residue + uInt status=0; // error code + + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, DECUNUSED, set)) + return decNumberZero(dn); + #endif + + do { // status & malloc protection + for (c=chars;; c++) { // -> input character + if (*c>='0' && *c<='9') { // test for Arabic digit + last=c; + d++; // count of real digits + continue; // still in decimal part + } + if (*c=='.' && dotchar==NULL) { // first '.' + dotchar=c; // record offset into decimal part + if (c==cfirst) cfirst++; // first digit must follow + continue;} + if (c==chars) { // first in string... + if (*c=='-') { // valid - sign + cfirst++; + bits=DECNEG; + continue;} + if (*c=='+') { // valid + sign + cfirst++; + continue;} + } + // *c is not a digit, or a valid +, -, or '.' + break; + } // c + + if (last==NULL) { // no digits yet + status=DEC_Conversion_syntax;// assume the worst + if (*c=='\0') break; // and no more to come... + #if DECSUBSET + // if subset then infinities and NaNs are not allowed + if (!set->extended) break; // hopeless + #endif + // Infinities and NaNs are possible, here + if (dotchar!=NULL) break; // .. unless had a dot + decNumberZero(dn); // be optimistic + if (decBiStr(c, "infinity", "INFINITY") + || decBiStr(c, "inf", "INF")) { + dn->bits=bits | DECINF; + status=0; // is OK + break; // all done + } + // a NaN expected + // 2003.09.10 NaNs are now permitted to have a sign + dn->bits=bits | DECNAN; // assume simple NaN + if (*c=='s' || *c=='S') { // looks like an sNaN + c++; + dn->bits=bits | DECSNAN; + } + if (*c!='n' && *c!='N') break; // check caseless "NaN" + c++; + if (*c!='a' && *c!='A') break; // .. + c++; + if (*c!='n' && *c!='N') break; // .. + c++; + // now either nothing, or nnnn payload, expected + // -> start of integer and skip leading 0s [including plain 0] + for (cfirst=c; *cfirst=='0';) cfirst++; + if (*cfirst=='\0') { // "NaN" or "sNaN", maybe with all 0s + status=0; // it's good + break; // .. + } + // something other than 0s; setup last and d as usual [no dots] + for (c=cfirst;; c++, d++) { + if (*c<'0' || *c>'9') break; // test for Arabic digit + last=c; + } + if (*c!='\0') break; // not all digits + if (d>set->digits-1) { + // [NB: payload in a decNumber can be full length unless + // clamped, in which case can only be digits-1] + if (set->clamp) break; + if (d>set->digits) break; + } // too many digits? + // good; drop through to convert the integer to coefficient + status=0; // syntax is OK + bits=dn->bits; // for copy-back + } // last==NULL + + else if (*c!='\0') { // more to process... + // had some digits; exponent is only valid sequence now + Flag nege; // 1=negative exponent + const char *firstexp; // -> first significant exponent digit + status=DEC_Conversion_syntax;// assume the worst + if (*c!='e' && *c!='E') break; + /* Found 'e' or 'E' -- now process explicit exponent */ + // 1998.07.11: sign no longer required + nege=0; + c++; // to (possible) sign + if (*c=='-') {nege=1; c++;} + else if (*c=='+') c++; + if (*c=='\0') break; + + for (; *c=='0' && *(c+1)!='\0';) c++; // strip insignificant zeros + firstexp=c; // save exponent digit place + for (; ;c++) { + if (*c<'0' || *c>'9') break; // not a digit + exponent=X10(exponent)+(Int)*c-(Int)'0'; + } // c + // if not now on a '\0', *c must not be a digit + if (*c!='\0') break; + + // (this next test must be after the syntax checks) + // if it was too long the exponent may have wrapped, so check + // carefully and set it to a certain overflow if wrap possible + if (c>=firstexp+9+1) { + if (c>firstexp+9+1 || *firstexp>'1') exponent=DECNUMMAXE*2; + // [up to 1999999999 is OK, for example 1E-1000000998] + } + if (nege) exponent=-exponent; // was negative + status=0; // is OK + } // stuff after digits + + // Here when whole string has been inspected; syntax is good + // cfirst->first digit (never dot), last->last digit (ditto) + + // strip leading zeros/dot [leave final 0 if all 0's] + if (*cfirst=='0') { // [cfirst has stepped over .] + for (c=cfirst; cextended) { + decNumberZero(dn); // clean result + break; // [could be return] + } + #endif + } // at least one leading 0 + + // Handle decimal point... + if (dotchar!=NULL && dotchardigits) res=dn->lsu; // fits into supplied decNumber + else { // rounding needed + Int needbytes=D2U(d)*sizeof(Unit);// bytes needed + res=resbuff; // assume use local buffer + if (needbytes>(Int)sizeof(resbuff)) { // too big for local + allocres=(Unit *)malloc(needbytes); + if (allocres==NULL) {status|=DEC_Insufficient_storage; break;} + res=allocres; + } + } + // res now -> number lsu, buffer, or allocated storage for Unit array + + // Place the coefficient into the selected Unit array + // [this is often 70% of the cost of this function when DECDPUN>1] + #if DECDPUN>1 + out=0; // accumulator + up=res+D2U(d)-1; // -> msu + cut=d-(up-res)*DECDPUN; // digits in top unit + for (c=cfirst;; c++) { // along the digits + if (*c=='.') continue; // ignore '.' [don't decrement cut] + out=X10(out)+(Int)*c-(Int)'0'; + if (c==last) break; // done [never get to trailing '.'] + cut--; + if (cut>0) continue; // more for this unit + *up=(Unit)out; // write unit + up--; // prepare for unit below.. + cut=DECDPUN; // .. + out=0; // .. + } // c + *up=(Unit)out; // write lsu + + #else + // DECDPUN==1 + up=res; // -> lsu + for (c=last; c>=cfirst; c--) { // over each character, from least + if (*c=='.') continue; // ignore . [don't step up] + *up=(Unit)((Int)*c-(Int)'0'); + up++; + } // c + #endif + + dn->bits=bits; + dn->exponent=exponent; + dn->digits=d; + + // if not in number (too long) shorten into the number + if (d>set->digits) { + residue=0; + decSetCoeff(dn, set, res, d, &residue, &status); + // always check for overflow or subnormal and round as needed + decFinalize(dn, set, &residue, &status); + } + else { // no rounding, but may still have overflow or subnormal + // [these tests are just for performance; finalize repeats them] + if ((dn->exponent-1emin-dn->digits) + || (dn->exponent-1>set->emax-set->digits)) { + residue=0; + decFinalize(dn, set, &residue, &status); + } + } + // decNumberShow(dn); + } while(0); // [for break] + + if (allocres!=NULL) free(allocres); // drop any storage used + if (status!=0) decStatus(dn, status, set); + return dn; + } /* decNumberFromString */ + +/* ================================================================== */ +/* Operators */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* decNumberAbs -- absolute value operator */ +/* */ +/* This computes C = abs(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* See also decNumberCopyAbs for a quiet bitwise version of this. */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This has the same effect as decNumberPlus unless A is negative, */ +/* in which case it has the same effect as decNumberMinus. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberAbs(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dzero; // for 0 + uInt status=0; // accumulator + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + decNumberZero(&dzero); // set 0 + dzero.exponent=rhs->exponent; // [no coefficient expansion] + decAddOp(res, &dzero, rhs, set, (uByte)(rhs->bits & DECNEG), &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberAbs + +/* ------------------------------------------------------------------ */ +/* decNumberAdd -- add two Numbers */ +/* */ +/* This computes C = A + B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This just calls the routine shared with Subtract */ +decNumber * decNumberAdd(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decAddOp(res, lhs, rhs, set, 0, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberAdd + +/* ------------------------------------------------------------------ */ +/* decNumberAnd -- AND two Numbers, digitwise */ +/* */ +/* This computes C = A & B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X&X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberAnd(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + const Unit *ua, *ub; // -> operands + const Unit *msua, *msub; // -> operand msus + Unit *uc, *msuc; // -> result and its msu + Int msudigs; // digits in res msu + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) + || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + + // operands are valid + ua=lhs->lsu; // bottom-up + ub=rhs->lsu; // .. + uc=res->lsu; // .. + msua=ua+D2U(lhs->digits)-1; // -> msu of lhs + msub=ub+D2U(rhs->digits)-1; // -> msu of rhs + msuc=uc+D2U(set->digits)-1; // -> msu of result + msudigs=MSUDIGITS(set->digits); // [faster than remainder] + for (; uc<=msuc; ua++, ub++, uc++) { // Unit loop + Unit a, b; // extract units + if (ua>msua) a=0; + else a=*ua; + if (ub>msub) b=0; + else b=*ub; + *uc=0; // can now write back + if (a|b) { // maybe 1 bits to examine + Int i, j; + *uc=0; // can now write back + // This loop could be unrolled and/or use BIN2BCD tables + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; // just did final digit + } // each digit + } // both OK + } // each unit + // [here uc-1 is the msu of the result] + res->digits=decGetDigits(res->lsu, uc-res->lsu); + res->exponent=0; // integer + res->bits=0; // sign=0 + return res; // [no status to set] + } // decNumberAnd + +/* ------------------------------------------------------------------ */ +/* decNumberCompare -- compare two Numbers */ +/* */ +/* This computes C = A ? B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit (or NaN). */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCompare(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPARE, &status); + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberCompare + +/* ------------------------------------------------------------------ */ +/* decNumberCompareSignal -- compare, signalling on all NaNs */ +/* */ +/* This computes C = A ? B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit (or NaN). */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCompareSignal(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPSIG, &status); + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberCompareSignal + +/* ------------------------------------------------------------------ */ +/* decNumberCompareTotal -- compare two Numbers, using total ordering */ +/* */ +/* This computes C = A ? B, under total ordering */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit; the result will always be one of */ +/* -1, 0, or 1. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCompareTotal(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status); + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberCompareTotal + +/* ------------------------------------------------------------------ */ +/* decNumberCompareTotalMag -- compare, total ordering of magnitudes */ +/* */ +/* This computes C = |A| ? |B|, under total ordering */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for one digit; the result will always be one of */ +/* -1, 0, or 1. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCompareTotalMag(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + uInt needbytes; // for space calculations + decNumber bufa[D2N(DECBUFFER+1)];// +1 in case DECBUFFER=0 + decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated + decNumber bufb[D2N(DECBUFFER+1)]; + decNumber *allocbufb=NULL; // -> allocated bufb, iff allocated + decNumber *a, *b; // temporary pointers + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { // protect allocated storage + // if either is negative, take a copy and absolute + if (decNumberIsNegative(lhs)) { // lhs<0 + a=bufa; + needbytes=sizeof(decNumber)+(D2U(lhs->digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { // need malloc space + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + a=allocbufa; // use the allocated space + } + decNumberCopy(a, lhs); // copy content + a->bits&=~DECNEG; // .. and clear the sign + lhs=a; // use copy from here on + } + if (decNumberIsNegative(rhs)) { // rhs<0 + b=bufb; + needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufb)) { // need malloc space + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufb==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + b=allocbufb; // use the allocated space + } + decNumberCopy(b, rhs); // copy content + b->bits&=~DECNEG; // .. and clear the sign + rhs=b; // use copy from here on + } + decCompareOp(res, lhs, rhs, set, COMPTOTAL, &status); + } while(0); // end protected + + if (allocbufa!=NULL) free(allocbufa); // drop any storage used + if (allocbufb!=NULL) free(allocbufb); // .. + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberCompareTotalMag + +/* ------------------------------------------------------------------ */ +/* decNumberDivide -- divide one number by another */ +/* */ +/* This computes C = A / B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X/X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberDivide(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decDivideOp(res, lhs, rhs, set, DIVIDE, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberDivide + +/* ------------------------------------------------------------------ */ +/* decNumberDivideInteger -- divide and return integer quotient */ +/* */ +/* This computes C = A # B, where # is the integer divide operator */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X#X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberDivideInteger(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decDivideOp(res, lhs, rhs, set, DIVIDEINT, &status); + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberDivideInteger + +/* ------------------------------------------------------------------ */ +/* decNumberExp -- exponentiation */ +/* */ +/* This computes C = exp(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* Finite results will always be full precision and Inexact, except */ +/* when A is a zero or -Infinity (giving 1 or 0 respectively). */ +/* */ +/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* This is a wrapper for decExpOp which can handle the slightly wider */ +/* (double) range needed by Ln (which has to be able to calculate */ +/* exp(-a) where a can be the tiniest number (Ntiny). */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberExp(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0; // accumulator + #if DECSUBSET + decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated + #endif + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // Check restrictions; these restrictions ensure that if h=8 (see + // decExpOp) then the result will either overflow or underflow to 0. + // Other math functions restrict the input range, too, for inverses. + // If not violated then carry out the operation. + if (!decCheckMath(rhs, set, &status)) do { // protect allocation + #if DECSUBSET + if (!set->extended) { + // reduce operand and set lostDigits status, as needed + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + decExpOp(res, rhs, set, &status); + } while(0); // end protected + + #if DECSUBSET + if (allocrhs !=NULL) free(allocrhs); // drop any storage used + #endif + // apply significant status + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberExp + +/* ------------------------------------------------------------------ */ +/* decNumberFMA -- fused multiply add */ +/* */ +/* This computes D = (A * B) + C with only one rounding */ +/* */ +/* res is D, the result. D may be A or B or C (e.g., X=FMA(X,X,X)) */ +/* lhs is A */ +/* rhs is B */ +/* fhs is C [far hand side] */ +/* set is the context */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberFMA(decNumber *res, const decNumber *lhs, + const decNumber *rhs, const decNumber *fhs, + decContext *set) { + uInt status=0; // accumulator + decContext dcmul; // context for the multiplication + uInt needbytes; // for space calculations + decNumber bufa[D2N(DECBUFFER*2+1)]; + decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated + decNumber *acc; // accumulator pointer + decNumber dzero; // work + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + if (decCheckOperands(res, fhs, DECUNUSED, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { // [undefined if subset] + status|=DEC_Invalid_operation; + break;} + #endif + // Check math restrictions [these ensure no overflow or underflow] + if ((!decNumberIsSpecial(lhs) && decCheckMath(lhs, set, &status)) + || (!decNumberIsSpecial(rhs) && decCheckMath(rhs, set, &status)) + || (!decNumberIsSpecial(fhs) && decCheckMath(fhs, set, &status))) break; + // set up context for multiply + dcmul=*set; + dcmul.digits=lhs->digits+rhs->digits; // just enough + // [The above may be an over-estimate for subset arithmetic, but that's OK] + dcmul.emax=DEC_MAX_EMAX; // effectively unbounded .. + dcmul.emin=DEC_MIN_EMIN; // [thanks to Math restrictions] + // set up decNumber space to receive the result of the multiply + acc=bufa; // may fit + needbytes=sizeof(decNumber)+(D2U(dcmul.digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { // need malloc space + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + acc=allocbufa; // use the allocated space + } + // multiply with extended range and necessary precision + //printf("emin=%ld\n", dcmul.emin); + decMultiplyOp(acc, lhs, rhs, &dcmul, &status); + // Only Invalid operation (from sNaN or Inf * 0) is possible in + // status; if either is seen than ignore fhs (in case it is + // another sNaN) and set acc to NaN unless we had an sNaN + // [decMultiplyOp leaves that to caller] + // Note sNaN has to go through addOp to shorten payload if + // necessary + if ((status&DEC_Invalid_operation)!=0) { + if (!(status&DEC_sNaN)) { // but be true invalid + decNumberZero(res); // acc not yet set + res->bits=DECNAN; + break; + } + decNumberZero(&dzero); // make 0 (any non-NaN would do) + fhs=&dzero; // use that + } + #if DECCHECK + else { // multiply was OK + if (status!=0) printf("Status=%08lx after FMA multiply\n", (LI)status); + } + #endif + // add the third operand and result -> res, and all is done + decAddOp(res, acc, fhs, set, 0, &status); + } while(0); // end protected + + if (allocbufa!=NULL) free(allocbufa); // drop any storage used + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberFMA + +/* ------------------------------------------------------------------ */ +/* decNumberInvert -- invert a Number, digitwise */ +/* */ +/* This computes C = ~A */ +/* */ +/* res is C, the result. C may be A (e.g., X=~X) */ +/* rhs is A */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberInvert(decNumber *res, const decNumber *rhs, + decContext *set) { + const Unit *ua, *msua; // -> operand and its msu + Unit *uc, *msuc; // -> result and its msu + Int msudigs; // digits in res msu + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + if (rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + // operand is valid + ua=rhs->lsu; // bottom-up + uc=res->lsu; // .. + msua=ua+D2U(rhs->digits)-1; // -> msu of rhs + msuc=uc+D2U(set->digits)-1; // -> msu of result + msudigs=MSUDIGITS(set->digits); // [faster than remainder] + for (; uc<=msuc; ua++, uc++) { // Unit loop + Unit a; // extract unit + Int i, j; // work + if (ua>msua) a=0; + else a=*ua; + *uc=0; // can now write back + // always need to examine all bits in rhs + // This loop could be unrolled and/or use BIN2BCD tables + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; // just did final digit + } // each digit + } // each unit + // [here uc-1 is the msu of the result] + res->digits=decGetDigits(res->lsu, uc-res->lsu); + res->exponent=0; // integer + res->bits=0; // sign=0 + return res; // [no status to set] + } // decNumberInvert + +/* ------------------------------------------------------------------ */ +/* decNumberLn -- natural logarithm */ +/* */ +/* This computes C = ln(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Notable cases: */ +/* A<0 -> Invalid */ +/* A=0 -> -Infinity (Exact) */ +/* A=+Infinity -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* This is a wrapper for decLnOp which can handle the slightly wider */ +/* (+11) range needed by Ln, Log10, etc. (which may have to be able */ +/* to calculate at p+e+2). */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberLn(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0; // accumulator + #if DECSUBSET + decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated + #endif + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // Check restrictions; this is a math function; if not violated + // then carry out the operation. + if (!decCheckMath(rhs, set, &status)) do { // protect allocation + #if DECSUBSET + if (!set->extended) { + // reduce operand and set lostDigits status, as needed + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + // special check in subset for rhs=0 + if (ISZERO(rhs)) { // +/- zeros -> error + status|=DEC_Invalid_operation; + break;} + } // extended=0 + #endif + decLnOp(res, rhs, set, &status); + } while(0); // end protected + + #if DECSUBSET + if (allocrhs !=NULL) free(allocrhs); // drop any storage used + #endif + // apply significant status + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberLn + +/* ------------------------------------------------------------------ */ +/* decNumberLogB - get adjusted exponent, by 754 rules */ +/* */ +/* This computes C = adjustedexponent(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context, used only for digits and status */ +/* */ +/* For an unrounded result, digits may need to be 10 (A might have */ +/* 10**9 digits and an exponent of +999999999, or one digit and an */ +/* exponent of -1999999999). */ +/* */ +/* This returns the adjusted exponent of A after (in theory) padding */ +/* with zeros on the right to set->digits digits while keeping the */ +/* same value. The exponent is not limited by emin/emax. */ +/* */ +/* Notable cases: */ +/* A<0 -> Use |A| */ +/* A=0 -> -Infinity (Division by zero) */ +/* A=Infinite -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* NaNs are propagated as usual */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberLogB(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0; // accumulator + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // NaNs as usual; Infinities return +Infinity; 0->oops + if (decNumberIsNaN(rhs)) decNaNs(res, rhs, NULL, set, &status); + else if (decNumberIsInfinite(rhs)) decNumberCopyAbs(res, rhs); + else if (decNumberIsZero(rhs)) { + decNumberZero(res); // prepare for Infinity + res->bits=DECNEG|DECINF; // -Infinity + status|=DEC_Division_by_zero; // as per 754 + } + else { // finite non-zero + Int ae=rhs->exponent+rhs->digits-1; // adjusted exponent + if (set->digits>=10) decNumberFromInt32(res, ae); // lay it out + else { + decNumber buft[D2N(10)]; // temporary number + decNumber *t=buft; // .. + decNumberFromInt32(t, ae); // lay it out + decNumberPlus(res, t, set); // round as necessary + } + } + + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberLogB + +/* ------------------------------------------------------------------ */ +/* decNumberLog10 -- logarithm in base 10 */ +/* */ +/* This computes C = log10(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Notable cases: */ +/* A<0 -> Invalid */ +/* A=0 -> -Infinity (Exact) */ +/* A=+Infinity -> +Infinity (Exact) */ +/* A=10**n (if n is an integer) -> n (Exact) */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* An Inexact result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* This calculates ln(A)/ln(10) using appropriate precision. For */ +/* ln(A) this is the max(p, rhs->digits + t) + 3, where p is the */ +/* requested digits and t is the number of digits in the exponent */ +/* (maximum 6). For ln(10) it is p + 3; this is often handled by the */ +/* fastpath in decLnOp. The final division is done to the requested */ +/* precision. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberLog10(decNumber *res, const decNumber *rhs, + decContext *set) { + uInt status=0, ignore=0; // status accumulators + uInt needbytes; // for space calculations + Int p; // working precision + Int t; // digits in exponent of A + + // buffers for a and b working decimals + // (adjustment calculator, same size) + decNumber bufa[D2N(DECBUFFER+2)]; + decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated + decNumber *a=bufa; // temporary a + decNumber bufb[D2N(DECBUFFER+2)]; + decNumber *allocbufb=NULL; // -> allocated bufb, iff allocated + decNumber *b=bufb; // temporary b + decNumber bufw[D2N(10)]; // working 2-10 digit number + decNumber *w=bufw; // .. + #if DECSUBSET + decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated + #endif + + decContext aset; // working context + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // Check restrictions; this is a math function; if not violated + // then carry out the operation. + if (!decCheckMath(rhs, set, &status)) do { // protect malloc + #if DECSUBSET + if (!set->extended) { + // reduce operand and set lostDigits status, as needed + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + // special check in subset for rhs=0 + if (ISZERO(rhs)) { // +/- zeros -> error + status|=DEC_Invalid_operation; + break;} + } // extended=0 + #endif + + decContextDefault(&aset, DEC_INIT_DECIMAL64); // clean context + + // handle exact powers of 10; only check if +ve finite + if (!(rhs->bits&(DECNEG|DECSPECIAL)) && !ISZERO(rhs)) { + Int residue=0; // (no residue) + uInt copystat=0; // clean status + + // round to a single digit... + aset.digits=1; + decCopyFit(w, rhs, &aset, &residue, ©stat); // copy & shorten + // if exact and the digit is 1, rhs is a power of 10 + if (!(copystat&DEC_Inexact) && w->lsu[0]==1) { + // the exponent, conveniently, is the power of 10; making + // this the result needs a little care as it might not fit, + // so first convert it into the working number, and then move + // to res + decNumberFromInt32(w, w->exponent); + residue=0; + decCopyFit(res, w, set, &residue, &status); // copy & round + decFinish(res, set, &residue, &status); // cleanup/set flags + break; + } // not a power of 10 + } // not a candidate for exact + + // simplify the information-content calculation to use 'total + // number of digits in a, including exponent' as compared to the + // requested digits, as increasing this will only rarely cost an + // iteration in ln(a) anyway + t=6; // it can never be >6 + + // allocate space when needed... + p=(rhs->digits+t>set->digits?rhs->digits+t:set->digits)+3; + needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { // need malloc space + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + a=allocbufa; // use the allocated space + } + aset.digits=p; // as calculated + aset.emax=DEC_MAX_MATH; // usual bounds + aset.emin=-DEC_MAX_MATH; // .. + aset.clamp=0; // and no concrete format + decLnOp(a, rhs, &aset, &status); // a=ln(rhs) + + // skip the division if the result so far is infinite, NaN, or + // zero, or there was an error; note NaN from sNaN needs copy + if (status&DEC_NaNs && !(status&DEC_sNaN)) break; + if (a->bits&DECSPECIAL || ISZERO(a)) { + decNumberCopy(res, a); // [will fit] + break;} + + // for ln(10) an extra 3 digits of precision are needed + p=set->digits+3; + needbytes=sizeof(decNumber)+(D2U(p)-1)*sizeof(Unit); + if (needbytes>sizeof(bufb)) { // need malloc space + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufb==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + b=allocbufb; // use the allocated space + } + decNumberZero(w); // set up 10... + #if DECDPUN==1 + w->lsu[1]=1; w->lsu[0]=0; // .. + #else + w->lsu[0]=10; // .. + #endif + w->digits=2; // .. + + aset.digits=p; + decLnOp(b, w, &aset, &ignore); // b=ln(10) + + aset.digits=set->digits; // for final divide + decDivideOp(res, a, b, &aset, DIVIDE, &status); // into result + } while(0); // [for break] + + if (allocbufa!=NULL) free(allocbufa); // drop any storage used + if (allocbufb!=NULL) free(allocbufb); // .. + #if DECSUBSET + if (allocrhs !=NULL) free(allocrhs); // .. + #endif + // apply significant status + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberLog10 + +/* ------------------------------------------------------------------ */ +/* decNumberMax -- compare two Numbers and return the maximum */ +/* */ +/* This computes C = A ? B, returning the maximum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberMax(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPMAX, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberMax + +/* ------------------------------------------------------------------ */ +/* decNumberMaxMag -- compare and return the maximum by magnitude */ +/* */ +/* This computes C = A ? B, returning the maximum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberMaxMag(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPMAXMAG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberMaxMag + +/* ------------------------------------------------------------------ */ +/* decNumberMin -- compare two Numbers and return the minimum */ +/* */ +/* This computes C = A ? B, returning the minimum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberMin(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPMIN, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberMin + +/* ------------------------------------------------------------------ */ +/* decNumberMinMag -- compare and return the minimum by magnitude */ +/* */ +/* This computes C = A ? B, returning the minimum by 754 rules */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberMinMag(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decCompareOp(res, lhs, rhs, set, COMPMINMAG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberMinMag + +/* ------------------------------------------------------------------ */ +/* decNumberMinus -- prefix minus operator */ +/* */ +/* This computes C = 0 - A */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* See also decNumberCopyNegate for a quiet bitwise version of this. */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* Simply use AddOp for the subtract, which will do the necessary. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberMinus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dzero; + uInt status=0; // accumulator + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + decNumberZero(&dzero); // make 0 + dzero.exponent=rhs->exponent; // [no coefficient expansion] + decAddOp(res, &dzero, rhs, set, DECNEG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberMinus + +/* ------------------------------------------------------------------ */ +/* decNumberNextMinus -- next towards -Infinity */ +/* */ +/* This computes C = A - infinitesimal, rounded towards -Infinity */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* This is a generalization of 754 NextDown. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberNextMinus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dtiny; // constant + decContext workset=*set; // work + uInt status=0; // accumulator + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // +Infinity is the special case + if ((rhs->bits&(DECINF|DECNEG))==DECINF) { + decSetMaxValue(res, set); // is +ve + // there is no status to set + return res; + } + decNumberZero(&dtiny); // start with 0 + dtiny.lsu[0]=1; // make number that is .. + dtiny.exponent=DEC_MIN_EMIN-1; // .. smaller than tiniest + workset.round=DEC_ROUND_FLOOR; + decAddOp(res, rhs, &dtiny, &workset, DECNEG, &status); + status&=DEC_Invalid_operation|DEC_sNaN; // only sNaN Invalid please + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberNextMinus + +/* ------------------------------------------------------------------ */ +/* decNumberNextPlus -- next towards +Infinity */ +/* */ +/* This computes C = A + infinitesimal, rounded towards +Infinity */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* This is a generalization of 754 NextUp. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberNextPlus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dtiny; // constant + decContext workset=*set; // work + uInt status=0; // accumulator + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // -Infinity is the special case + if ((rhs->bits&(DECINF|DECNEG))==(DECINF|DECNEG)) { + decSetMaxValue(res, set); + res->bits=DECNEG; // negative + // there is no status to set + return res; + } + decNumberZero(&dtiny); // start with 0 + dtiny.lsu[0]=1; // make number that is .. + dtiny.exponent=DEC_MIN_EMIN-1; // .. smaller than tiniest + workset.round=DEC_ROUND_CEILING; + decAddOp(res, rhs, &dtiny, &workset, 0, &status); + status&=DEC_Invalid_operation|DEC_sNaN; // only sNaN Invalid please + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberNextPlus + +/* ------------------------------------------------------------------ */ +/* decNumberNextToward -- next towards rhs */ +/* */ +/* This computes C = A +/- infinitesimal, rounded towards */ +/* +/-Infinity in the direction of B, as per 754-1985 nextafter */ +/* modified during revision but dropped from 754-2008. */ +/* */ +/* res is C, the result. C may be A or B. */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* This is a generalization of 754-1985 NextAfter. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberNextToward(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + decNumber dtiny; // constant + decContext workset=*set; // work + Int result; // .. + uInt status=0; // accumulator + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { + decNaNs(res, lhs, rhs, set, &status); + } + else { // Is numeric, so no chance of sNaN Invalid, etc. + result=decCompare(lhs, rhs, 0); // sign matters + if (result==BADINT) status|=DEC_Insufficient_storage; // rare + else { // valid compare + if (result==0) decNumberCopySign(res, lhs, rhs); // easy + else { // differ: need NextPlus or NextMinus + uByte sub; // add or subtract + if (result<0) { // lhsbits&(DECINF|DECNEG))==(DECINF|DECNEG)) { + decSetMaxValue(res, set); + res->bits=DECNEG; // negative + return res; // there is no status to set + } + workset.round=DEC_ROUND_CEILING; + sub=0; // add, please + } // plus + else { // lhs>rhs, do nextminus + // +Infinity is the special case + if ((lhs->bits&(DECINF|DECNEG))==DECINF) { + decSetMaxValue(res, set); + return res; // there is no status to set + } + workset.round=DEC_ROUND_FLOOR; + sub=DECNEG; // subtract, please + } // minus + decNumberZero(&dtiny); // start with 0 + dtiny.lsu[0]=1; // make number that is .. + dtiny.exponent=DEC_MIN_EMIN-1; // .. smaller than tiniest + decAddOp(res, lhs, &dtiny, &workset, sub, &status); // + or - + // turn off exceptions if the result is a normal number + // (including Nmin), otherwise let all status through + if (decNumberIsNormal(res, set)) status=0; + } // unequal + } // compare OK + } // numeric + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberNextToward + +/* ------------------------------------------------------------------ */ +/* decNumberOr -- OR two Numbers, digitwise */ +/* */ +/* This computes C = A | B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X|X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberOr(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + const Unit *ua, *ub; // -> operands + const Unit *msua, *msub; // -> operand msus + Unit *uc, *msuc; // -> result and its msu + Int msudigs; // digits in res msu + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) + || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + // operands are valid + ua=lhs->lsu; // bottom-up + ub=rhs->lsu; // .. + uc=res->lsu; // .. + msua=ua+D2U(lhs->digits)-1; // -> msu of lhs + msub=ub+D2U(rhs->digits)-1; // -> msu of rhs + msuc=uc+D2U(set->digits)-1; // -> msu of result + msudigs=MSUDIGITS(set->digits); // [faster than remainder] + for (; uc<=msuc; ua++, ub++, uc++) { // Unit loop + Unit a, b; // extract units + if (ua>msua) a=0; + else a=*ua; + if (ub>msub) b=0; + else b=*ub; + *uc=0; // can now write back + if (a|b) { // maybe 1 bits to examine + Int i, j; + // This loop could be unrolled and/or use BIN2BCD tables + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; // just did final digit + } // each digit + } // non-zero + } // each unit + // [here uc-1 is the msu of the result] + res->digits=decGetDigits(res->lsu, uc-res->lsu); + res->exponent=0; // integer + res->bits=0; // sign=0 + return res; // [no status to set] + } // decNumberOr + +/* ------------------------------------------------------------------ */ +/* decNumberPlus -- prefix plus operator */ +/* */ +/* This computes C = 0 + A */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* See also decNumberCopy for a quiet bitwise version of this. */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This simply uses AddOp; Add will take fast path after preparing A. */ +/* Performance is a concern here, as this routine is often used to */ +/* check operands and apply rounding and overflow/underflow testing. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberPlus(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dzero; + uInt status=0; // accumulator + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + decNumberZero(&dzero); // make 0 + dzero.exponent=rhs->exponent; // [no coefficient expansion] + decAddOp(res, &dzero, rhs, set, 0, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberPlus + +/* ------------------------------------------------------------------ */ +/* decNumberMultiply -- multiply two Numbers */ +/* */ +/* This computes C = A x B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberMultiply(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decMultiplyOp(res, lhs, rhs, set, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberMultiply + +/* ------------------------------------------------------------------ */ +/* decNumberPower -- raise a number to a power */ +/* */ +/* This computes C = A ** B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X**X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Mathematical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* */ +/* However, if 1999999997<=B<=999999999 and B is an integer then the */ +/* restrictions on A and the context are relaxed to the usual bounds, */ +/* for compatibility with the earlier (integer power only) version */ +/* of this function. */ +/* */ +/* When B is an integer, the result may be exact, even if rounded. */ +/* */ +/* The final result is rounded according to the context; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberPower(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + #if DECSUBSET + decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated + decNumber *allocrhs=NULL; // .., rhs + #endif + decNumber *allocdac=NULL; // -> allocated acc buffer, iff used + decNumber *allocinv=NULL; // -> allocated 1/x buffer, iff used + Int reqdigits=set->digits; // requested DIGITS + Int n; // rhs in binary + Flag rhsint=0; // 1 if rhs is an integer + Flag useint=0; // 1 if can use integer calculation + Flag isoddint=0; // 1 if rhs is an integer and odd + Int i; // work + #if DECSUBSET + Int dropped; // .. + #endif + uInt needbytes; // buffer size needed + Flag seenbit; // seen a bit while powering + Int residue=0; // rounding residue + uInt status=0; // accumulators + uByte bits=0; // result sign if errors + decContext aset; // working context + decNumber dnOne; // work value 1... + // local accumulator buffer [a decNumber, with digits+elength+1 digits] + decNumber dacbuff[D2N(DECBUFFER+9)]; + decNumber *dac=dacbuff; // -> result accumulator + // same again for possible 1/lhs calculation + decNumber invbuff[D2N(DECBUFFER+9)]; + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { // reduce operands and set status, as needed + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, &status); + if (alloclhs==NULL) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + // handle NaNs and rhs Infinity (lhs infinity is harder) + if (SPECIALARGS) { + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) { // NaNs + decNaNs(res, lhs, rhs, set, &status); + break;} + if (decNumberIsInfinite(rhs)) { // rhs Infinity + Flag rhsneg=rhs->bits&DECNEG; // save rhs sign + if (decNumberIsNegative(lhs) // lhs<0 + && !decNumberIsZero(lhs)) // .. + status|=DEC_Invalid_operation; + else { // lhs >=0 + decNumberZero(&dnOne); // set up 1 + dnOne.lsu[0]=1; + decNumberCompare(dac, lhs, &dnOne, set); // lhs ? 1 + decNumberZero(res); // prepare for 0/1/Infinity + if (decNumberIsNegative(dac)) { // lhs<1 + if (rhsneg) res->bits|=DECINF; // +Infinity [else is +0] + } + else if (dac->lsu[0]==0) { // lhs=1 + // 1**Infinity is inexact, so return fully-padded 1.0000 + Int shift=set->digits-1; + *res->lsu=1; // was 0, make int 1 + res->digits=decShiftToMost(res->lsu, 1, shift); + res->exponent=-shift; // make 1.0000... + status|=DEC_Inexact|DEC_Rounded; // deemed inexact + } + else { // lhs>1 + if (!rhsneg) res->bits|=DECINF; // +Infinity [else is +0] + } + } // lhs>=0 + break;} + // [lhs infinity drops through] + } // specials + + // Original rhs may be an integer that fits and is in range + n=decGetInt(rhs); + if (n!=BADINT) { // it is an integer + rhsint=1; // record the fact for 1**n + isoddint=(Flag)n&1; // [works even if big] + if (n!=BIGEVEN && n!=BIGODD) // can use integer path? + useint=1; // looks good + } + + if (decNumberIsNegative(lhs) // -x .. + && isoddint) bits=DECNEG; // .. to an odd power + + // handle LHS infinity + if (decNumberIsInfinite(lhs)) { // [NaNs already handled] + uByte rbits=rhs->bits; // save + decNumberZero(res); // prepare + if (n==0) *res->lsu=1; // [-]Inf**0 => 1 + else { + // -Inf**nonint -> error + if (!rhsint && decNumberIsNegative(lhs)) { + status|=DEC_Invalid_operation; // -Inf**nonint is error + break;} + if (!(rbits & DECNEG)) bits|=DECINF; // was not a **-n + // [otherwise will be 0 or -0] + res->bits=bits; + } + break;} + + // similarly handle LHS zero + if (decNumberIsZero(lhs)) { + if (n==0) { // 0**0 => Error + #if DECSUBSET + if (!set->extended) { // [unless subset] + decNumberZero(res); + *res->lsu=1; // return 1 + break;} + #endif + status|=DEC_Invalid_operation; + } + else { // 0**x + uByte rbits=rhs->bits; // save + if (rbits & DECNEG) { // was a 0**(-n) + #if DECSUBSET + if (!set->extended) { // [bad if subset] + status|=DEC_Invalid_operation; + break;} + #endif + bits|=DECINF; + } + decNumberZero(res); // prepare + // [otherwise will be 0 or -0] + res->bits=bits; + } + break;} + + // here both lhs and rhs are finite; rhs==0 is handled in the + // integer path. Next handle the non-integer cases + if (!useint) { // non-integral rhs + // any -ve lhs is bad, as is either operand or context out of + // bounds + if (decNumberIsNegative(lhs)) { + status|=DEC_Invalid_operation; + break;} + if (decCheckMath(lhs, set, &status) + || decCheckMath(rhs, set, &status)) break; // variable status + + decContextDefault(&aset, DEC_INIT_DECIMAL64); // clean context + aset.emax=DEC_MAX_MATH; // usual bounds + aset.emin=-DEC_MAX_MATH; // .. + aset.clamp=0; // and no concrete format + + // calculate the result using exp(ln(lhs)*rhs), which can + // all be done into the accumulator, dac. The precision needed + // is enough to contain the full information in the lhs (which + // is the total digits, including exponent), or the requested + // precision, if larger, + 4; 6 is used for the exponent + // maximum length, and this is also used when it is shorter + // than the requested digits as it greatly reduces the >0.5 ulp + // cases at little cost (because Ln doubles digits each + // iteration so a few extra digits rarely causes an extra + // iteration) + aset.digits=MAXI(lhs->digits, set->digits)+6+4; + } // non-integer rhs + + else { // rhs is in-range integer + if (n==0) { // x**0 = 1 + // (0**0 was handled above) + decNumberZero(res); // result=1 + *res->lsu=1; // .. + break;} + // rhs is a non-zero integer + if (n<0) n=-n; // use abs(n) + + aset=*set; // clone the context + aset.round=DEC_ROUND_HALF_EVEN; // internally use balanced + // calculate the working DIGITS + aset.digits=reqdigits+(rhs->digits+rhs->exponent)+2; + #if DECSUBSET + if (!set->extended) aset.digits--; // use classic precision + #endif + // it's an error if this is more than can be handled + if (aset.digits>DECNUMMAXP) {status|=DEC_Invalid_operation; break;} + } // integer path + + // aset.digits is the count of digits for the accumulator needed + // if accumulator is too long for local storage, then allocate + needbytes=sizeof(decNumber)+(D2U(aset.digits)-1)*sizeof(Unit); + // [needbytes also used below if 1/lhs needed] + if (needbytes>sizeof(dacbuff)) { + allocdac=(decNumber *)malloc(needbytes); + if (allocdac==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + dac=allocdac; // use the allocated space + } + // here, aset is set up and accumulator is ready for use + + if (!useint) { // non-integral rhs + // x ** y; special-case x=1 here as it will otherwise always + // reduce to integer 1; decLnOp has a fastpath which detects + // the case of x=1 + decLnOp(dac, lhs, &aset, &status); // dac=ln(lhs) + // [no error possible, as lhs 0 already handled] + if (ISZERO(dac)) { // x==1, 1.0, etc. + // need to return fully-padded 1.0000 etc., but rhsint->1 + *dac->lsu=1; // was 0, make int 1 + if (!rhsint) { // add padding + Int shift=set->digits-1; + dac->digits=decShiftToMost(dac->lsu, 1, shift); + dac->exponent=-shift; // make 1.0000... + status|=DEC_Inexact|DEC_Rounded; // deemed inexact + } + } + else { + decMultiplyOp(dac, dac, rhs, &aset, &status); // dac=dac*rhs + decExpOp(dac, dac, &aset, &status); // dac=exp(dac) + } + // and drop through for final rounding + } // non-integer rhs + + else { // carry on with integer + decNumberZero(dac); // acc=1 + *dac->lsu=1; // .. + + // if a negative power the constant 1 is needed, and if not subset + // invert the lhs now rather than inverting the result later + if (decNumberIsNegative(rhs)) { // was a **-n [hence digits>0] + decNumber *inv=invbuff; // asssume use fixed buffer + decNumberCopy(&dnOne, dac); // dnOne=1; [needed now or later] + #if DECSUBSET + if (set->extended) { // need to calculate 1/lhs + #endif + // divide lhs into 1, putting result in dac [dac=1/dac] + decDivideOp(dac, &dnOne, lhs, &aset, DIVIDE, &status); + // now locate or allocate space for the inverted lhs + if (needbytes>sizeof(invbuff)) { + allocinv=(decNumber *)malloc(needbytes); + if (allocinv==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + inv=allocinv; // use the allocated space + } + // [inv now points to big-enough buffer or allocated storage] + decNumberCopy(inv, dac); // copy the 1/lhs + decNumberCopy(dac, &dnOne); // restore acc=1 + lhs=inv; // .. and go forward with new lhs + #if DECSUBSET + } + #endif + } + + // Raise-to-the-power loop... + seenbit=0; // set once a 1-bit is encountered + for (i=1;;i++){ // for each bit [top bit ignored] + // abandon if had overflow or terminal underflow + if (status & (DEC_Overflow|DEC_Underflow)) { // interesting? + if (status&DEC_Overflow || ISZERO(dac)) break; + } + // [the following two lines revealed an optimizer bug in a C++ + // compiler, with symptom: 5**3 -> 25, when n=n+n was used] + n=n<<1; // move next bit to testable position + if (n<0) { // top bit is set + seenbit=1; // OK, significant bit seen + decMultiplyOp(dac, dac, lhs, &aset, &status); // dac=dac*x + } + if (i==31) break; // that was the last bit + if (!seenbit) continue; // no need to square 1 + decMultiplyOp(dac, dac, dac, &aset, &status); // dac=dac*dac [square] + } /*i*/ // 32 bits + + // complete internal overflow or underflow processing + if (status & (DEC_Overflow|DEC_Underflow)) { + #if DECSUBSET + // If subset, and power was negative, reverse the kind of -erflow + // [1/x not yet done] + if (!set->extended && decNumberIsNegative(rhs)) { + if (status & DEC_Overflow) + status^=DEC_Overflow | DEC_Underflow | DEC_Subnormal; + else { // trickier -- Underflow may or may not be set + status&=~(DEC_Underflow | DEC_Subnormal); // [one or both] + status|=DEC_Overflow; + } + } + #endif + dac->bits=(dac->bits & ~DECNEG) | bits; // force correct sign + // round subnormals [to set.digits rather than aset.digits] + // or set overflow result similarly as required + decFinalize(dac, set, &residue, &status); + decNumberCopy(res, dac); // copy to result (is now OK length) + break; + } + + #if DECSUBSET + if (!set->extended && // subset math + decNumberIsNegative(rhs)) { // was a **-n [hence digits>0] + // so divide result into 1 [dac=1/dac] + decDivideOp(dac, &dnOne, dac, &aset, DIVIDE, &status); + } + #endif + } // rhs integer path + + // reduce result to the requested length and copy to result + decCopyFit(res, dac, set, &residue, &status); + decFinish(res, set, &residue, &status); // final cleanup + #if DECSUBSET + if (!set->extended) decTrim(res, set, 0, 1, &dropped); // trailing zeros + #endif + } while(0); // end protected + + if (allocdac!=NULL) free(allocdac); // drop any storage used + if (allocinv!=NULL) free(allocinv); // .. + #if DECSUBSET + if (alloclhs!=NULL) free(alloclhs); // .. + if (allocrhs!=NULL) free(allocrhs); // .. + #endif + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberPower + +/* ------------------------------------------------------------------ */ +/* decNumberQuantize -- force exponent to requested value */ +/* */ +/* This computes C = op(A, B), where op adjusts the coefficient */ +/* of C (by rounding or shifting) such that the exponent (-scale) */ +/* of C has exponent of B. The numerical value of C will equal A, */ +/* except for the effects of any rounding that occurred. */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the number with exponent to match */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* after the operation is guaranteed to be equal to that of B. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberQuantize(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decQuantizeOp(res, lhs, rhs, set, 1, &status); + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberQuantize + +/* ------------------------------------------------------------------ */ +/* decNumberReduce -- remove trailing zeros */ +/* */ +/* This computes C = 0 + A, and normalizes the result */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +// Previously known as Normalize +decNumber * decNumberNormalize(decNumber *res, const decNumber *rhs, + decContext *set) { + return decNumberReduce(res, rhs, set); + } // decNumberNormalize + +decNumber * decNumberReduce(decNumber *res, const decNumber *rhs, + decContext *set) { + #if DECSUBSET + decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated + #endif + uInt status=0; // as usual + Int residue=0; // as usual + Int dropped; // work + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operand and set lostDigits status, as needed + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + // Infinities copy through; NaNs need usual treatment + if (decNumberIsNaN(rhs)) { + decNaNs(res, rhs, NULL, set, &status); + break; + } + + // reduce result to the requested length and copy to result + decCopyFit(res, rhs, set, &residue, &status); // copy & round + decFinish(res, set, &residue, &status); // cleanup/set flags + decTrim(res, set, 1, 0, &dropped); // normalize in place + // [may clamp] + } while(0); // end protected + + #if DECSUBSET + if (allocrhs !=NULL) free(allocrhs); // .. + #endif + if (status!=0) decStatus(res, status, set);// then report status + return res; + } // decNumberReduce + +/* ------------------------------------------------------------------ */ +/* decNumberRescale -- force exponent to requested value */ +/* */ +/* This computes C = op(A, B), where op adjusts the coefficient */ +/* of C (by rounding or shifting) such that the exponent (-scale) */ +/* of C has the value B. The numerical value of C will equal A, */ +/* except for the effects of any rounding that occurred. */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the requested exponent */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* after the operation is guaranteed to be equal to B. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberRescale(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decQuantizeOp(res, lhs, rhs, set, 0, &status); + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberRescale + +/* ------------------------------------------------------------------ */ +/* decNumberRemainder -- divide and return remainder */ +/* */ +/* This computes C = A % B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X%X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberRemainder(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decDivideOp(res, lhs, rhs, set, REMAINDER, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberRemainder + +/* ------------------------------------------------------------------ */ +/* decNumberRemainderNear -- divide and return remainder from nearest */ +/* */ +/* This computes C = A % B, where % is the IEEE remainder operator */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X%X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberRemainderNear(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + decDivideOp(res, lhs, rhs, set, REMNEAR, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberRemainderNear + +/* ------------------------------------------------------------------ */ +/* decNumberRotate -- rotate the coefficient of a Number left/right */ +/* */ +/* This computes C = A rot B (in base ten and rotating set->digits */ +/* digits). */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=XrotX) */ +/* lhs is A */ +/* rhs is B, the number of digits to rotate (-ve to right) */ +/* set is the context */ +/* */ +/* The digits of the coefficient of A are rotated to the left (if B */ +/* is positive) or to the right (if B is negative) without adjusting */ +/* the exponent or the sign of A. If lhs->digits is less than */ +/* set->digits the coefficient is padded with zeros on the left */ +/* before the rotate. Any leading zeros in the result are removed */ +/* as usual. */ +/* */ +/* B must be an integer (q=0) and in the range -set->digits through */ +/* +set->digits. */ +/* C must have space for set->digits digits. */ +/* NaNs are propagated as usual. Infinities are unaffected (but */ +/* B must be valid). No status is set unless B is invalid or an */ +/* operand is an sNaN. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberRotate(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + Int rotate; // rhs as an Int + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + // NaNs propagate as normal + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) + decNaNs(res, lhs, rhs, set, &status); + // rhs must be an integer + else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) + status=DEC_Invalid_operation; + else { // both numeric, rhs is an integer + rotate=decGetInt(rhs); // [cannot fail] + if (rotate==BADINT // something bad .. + || rotate==BIGODD || rotate==BIGEVEN // .. very big .. + || abs(rotate)>set->digits) // .. or out of range + status=DEC_Invalid_operation; + else { // rhs is OK + decNumberCopy(res, lhs); + // convert -ve rotate to equivalent positive rotation + if (rotate<0) rotate=set->digits+rotate; + if (rotate!=0 && rotate!=set->digits // zero or full rotation + && !decNumberIsInfinite(res)) { // lhs was infinite + // left-rotate to do; 0 < rotate < set->digits + uInt units, shift; // work + uInt msudigits; // digits in result msu + Unit *msu=res->lsu+D2U(res->digits)-1; // current msu + Unit *msumax=res->lsu+D2U(set->digits)-1; // rotation msu + for (msu++; msu<=msumax; msu++) *msu=0; // ensure high units=0 + res->digits=set->digits; // now full-length + msudigits=MSUDIGITS(res->digits); // actual digits in msu + + // rotation here is done in-place, in three steps + // 1. shift all to least up to one unit to unit-align final + // lsd [any digits shifted out are rotated to the left, + // abutted to the original msd (which may require split)] + // + // [if there are no whole units left to rotate, the + // rotation is now complete] + // + // 2. shift to least, from below the split point only, so that + // the final msd is in the right place in its Unit [any + // digits shifted out will fit exactly in the current msu, + // left aligned, no split required] + // + // 3. rotate all the units by reversing left part, right + // part, and then whole + // + // example: rotate right 8 digits (2 units + 2), DECDPUN=3. + // + // start: 00a bcd efg hij klm npq + // + // 1a 000 0ab cde fgh|ijk lmn [pq saved] + // 1b 00p qab cde fgh|ijk lmn + // + // 2a 00p qab cde fgh|00i jkl [mn saved] + // 2b mnp qab cde fgh|00i jkl + // + // 3a fgh cde qab mnp|00i jkl + // 3b fgh cde qab mnp|jkl 00i + // 3c 00i jkl mnp qab cde fgh + + // Step 1: amount to shift is the partial right-rotate count + rotate=set->digits-rotate; // make it right-rotate + units=rotate/DECDPUN; // whole units to rotate + shift=rotate%DECDPUN; // left-over digits count + if (shift>0) { // not an exact number of units + uInt save=res->lsu[0]%powers[shift]; // save low digit(s) + decShiftToLeast(res->lsu, D2U(res->digits), shift); + if (shift>msudigits) { // msumax-1 needs >0 digits + uInt rem=save%powers[shift-msudigits];// split save + *msumax=(Unit)(save/powers[shift-msudigits]); // and insert + *(msumax-1)=*(msumax-1) + +(Unit)(rem*powers[DECDPUN-(shift-msudigits)]); // .. + } + else { // all fits in msumax + *msumax=*msumax+(Unit)(save*powers[msudigits-shift]); // [maybe *1] + } + } // digits shift needed + + // If whole units to rotate... + if (units>0) { // some to do + // Step 2: the units to touch are the whole ones in rotate, + // if any, and the shift is DECDPUN-msudigits (which may be + // 0, again) + shift=DECDPUN-msudigits; + if (shift>0) { // not an exact number of units + uInt save=res->lsu[0]%powers[shift]; // save low digit(s) + decShiftToLeast(res->lsu, units, shift); + *msumax=*msumax+(Unit)(save*powers[msudigits]); + } // partial shift needed + + // Step 3: rotate the units array using triple reverse + // (reversing is easy and fast) + decReverse(res->lsu+units, msumax); // left part + decReverse(res->lsu, res->lsu+units-1); // right part + decReverse(res->lsu, msumax); // whole + } // whole units to rotate + // the rotation may have left an undetermined number of zeros + // on the left, so true length needs to be calculated + res->digits=decGetDigits(res->lsu, msumax-res->lsu+1); + } // rotate needed + } // rhs OK + } // numerics + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberRotate + +/* ------------------------------------------------------------------ */ +/* decNumberSameQuantum -- test for equal exponents */ +/* */ +/* res is the result number, which will contain either 0 or 1 */ +/* lhs is a number to test */ +/* rhs is the second (usually a pattern) */ +/* */ +/* No errors are possible and no context is needed. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberSameQuantum(decNumber *res, const decNumber *lhs, + const decNumber *rhs) { + Unit ret=0; // return value + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, DECUNCONT)) return res; + #endif + + if (SPECIALARGS) { + if (decNumberIsNaN(lhs) && decNumberIsNaN(rhs)) ret=1; + else if (decNumberIsInfinite(lhs) && decNumberIsInfinite(rhs)) ret=1; + // [anything else with a special gives 0] + } + else if (lhs->exponent==rhs->exponent) ret=1; + + decNumberZero(res); // OK to overwrite an operand now + *res->lsu=ret; + return res; + } // decNumberSameQuantum + +/* ------------------------------------------------------------------ */ +/* decNumberScaleB -- multiply by a power of 10 */ +/* */ +/* This computes C = A x 10**B where B is an integer (q=0) with */ +/* maximum magnitude 2*(emax+digits) */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the requested power of ten to use */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* The result may underflow or overflow. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberScaleB(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + Int reqexp; // requested exponent change [B] + uInt status=0; // accumulator + Int residue; // work + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + // Handle special values except lhs infinite + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) + decNaNs(res, lhs, rhs, set, &status); + // rhs must be an integer + else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) + status=DEC_Invalid_operation; + else { + // lhs is a number; rhs is a finite with q==0 + reqexp=decGetInt(rhs); // [cannot fail] + // maximum range is larger than getInt can handle, so this is + // more restrictive than the specification + if (reqexp==BADINT // something bad .. + || reqexp==BIGODD || reqexp==BIGEVEN // it was huge + || (abs(reqexp)+1)/2>(set->digits+set->emax)) // .. or out of range + status=DEC_Invalid_operation; + else { // rhs is OK + decNumberCopy(res, lhs); // all done if infinite lhs + if (!decNumberIsInfinite(res)) { // prepare to scale + Int exp=res->exponent; // save for overflow test + res->exponent+=reqexp; // adjust the exponent + if (((exp^reqexp)>=0) // same sign ... + && ((exp^res->exponent)<0)) { // .. but result had different + // the calculation overflowed, so force right treatment + if (exp<0) res->exponent=DEC_MIN_EMIN-DEC_MAX_DIGITS; + else res->exponent=DEC_MAX_EMAX+1; + } + residue=0; + decFinalize(res, set, &residue, &status); // final check + } // finite LHS + } // rhs OK + } // rhs finite + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberScaleB + +/* ------------------------------------------------------------------ */ +/* decNumberShift -- shift the coefficient of a Number left or right */ +/* */ +/* This computes C = A << B or C = A >> -B (in base ten). */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X<digits through */ +/* +set->digits. */ +/* C must have space for set->digits digits. */ +/* NaNs are propagated as usual. Infinities are unaffected (but */ +/* B must be valid). No status is set unless B is invalid or an */ +/* operand is an sNaN. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberShift(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + Int shift; // rhs as an Int + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + // NaNs propagate as normal + if (decNumberIsNaN(lhs) || decNumberIsNaN(rhs)) + decNaNs(res, lhs, rhs, set, &status); + // rhs must be an integer + else if (decNumberIsInfinite(rhs) || rhs->exponent!=0) + status=DEC_Invalid_operation; + else { // both numeric, rhs is an integer + shift=decGetInt(rhs); // [cannot fail] + if (shift==BADINT // something bad .. + || shift==BIGODD || shift==BIGEVEN // .. very big .. + || abs(shift)>set->digits) // .. or out of range + status=DEC_Invalid_operation; + else { // rhs is OK + decNumberCopy(res, lhs); + if (shift!=0 && !decNumberIsInfinite(res)) { // something to do + if (shift>0) { // to left + if (shift==set->digits) { // removing all + *res->lsu=0; // so place 0 + res->digits=1; // .. + } + else { // + // first remove leading digits if necessary + if (res->digits+shift>set->digits) { + decDecap(res, res->digits+shift-set->digits); + // that updated res->digits; may have gone to 1 (for a + // single digit or for zero + } + if (res->digits>1 || *res->lsu) // if non-zero.. + res->digits=decShiftToMost(res->lsu, res->digits, shift); + } // partial left + } // left + else { // to right + if (-shift>=res->digits) { // discarding all + *res->lsu=0; // so place 0 + res->digits=1; // .. + } + else { + decShiftToLeast(res->lsu, D2U(res->digits), -shift); + res->digits-=(-shift); + } + } // to right + } // non-0 non-Inf shift + } // rhs OK + } // numerics + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberShift + +/* ------------------------------------------------------------------ */ +/* decNumberSquareRoot -- square root operator */ +/* */ +/* This computes C = squareroot(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +/* This uses the following varying-precision algorithm in: */ +/* */ +/* Properly Rounded Variable Precision Square Root, T. E. Hull and */ +/* A. Abrham, ACM Transactions on Mathematical Software, Vol 11 #3, */ +/* pp229-237, ACM, September 1985. */ +/* */ +/* The square-root is calculated using Newton's method, after which */ +/* a check is made to ensure the result is correctly rounded. */ +/* */ +/* % [Reformatted original Numerical Turing source code follows.] */ +/* function sqrt(x : real) : real */ +/* % sqrt(x) returns the properly rounded approximation to the square */ +/* % root of x, in the precision of the calling environment, or it */ +/* % fails if x < 0. */ +/* % t e hull and a abrham, august, 1984 */ +/* if x <= 0 then */ +/* if x < 0 then */ +/* assert false */ +/* else */ +/* result 0 */ +/* end if */ +/* end if */ +/* var f := setexp(x, 0) % fraction part of x [0.1 <= x < 1] */ +/* var e := getexp(x) % exponent part of x */ +/* var approx : real */ +/* if e mod 2 = 0 then */ +/* approx := .259 + .819 * f % approx to root of f */ +/* else */ +/* f := f/l0 % adjustments */ +/* e := e + 1 % for odd */ +/* approx := .0819 + 2.59 * f % exponent */ +/* end if */ +/* */ +/* var p:= 3 */ +/* const maxp := currentprecision + 2 */ +/* loop */ +/* p := min(2*p - 2, maxp) % p = 4,6,10, . . . , maxp */ +/* precision p */ +/* approx := .5 * (approx + f/approx) */ +/* exit when p = maxp */ +/* end loop */ +/* */ +/* % approx is now within 1 ulp of the properly rounded square root */ +/* % of f; to ensure proper rounding, compare squares of (approx - */ +/* % l/2 ulp) and (approx + l/2 ulp) with f. */ +/* p := currentprecision */ +/* begin */ +/* precision p + 2 */ +/* const approxsubhalf := approx - setexp(.5, -p) */ +/* if mulru(approxsubhalf, approxsubhalf) > f then */ +/* approx := approx - setexp(.l, -p + 1) */ +/* else */ +/* const approxaddhalf := approx + setexp(.5, -p) */ +/* if mulrd(approxaddhalf, approxaddhalf) < f then */ +/* approx := approx + setexp(.l, -p + 1) */ +/* end if */ +/* end if */ +/* end */ +/* result setexp(approx, e div 2) % fix exponent */ +/* end sqrt */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberSquareRoot(decNumber *res, const decNumber *rhs, + decContext *set) { + decContext workset, approxset; // work contexts + decNumber dzero; // used for constant zero + Int maxp; // largest working precision + Int workp; // working precision + Int residue=0; // rounding residue + uInt status=0, ignore=0; // status accumulators + uInt rstatus; // .. + Int exp; // working exponent + Int ideal; // ideal (preferred) exponent + Int needbytes; // work + Int dropped; // .. + + #if DECSUBSET + decNumber *allocrhs=NULL; // non-NULL if rounded rhs allocated + #endif + // buffer for f [needs +1 in case DECBUFFER 0] + decNumber buff[D2N(DECBUFFER+1)]; + // buffer for a [needs +2 to match likely maxp] + decNumber bufa[D2N(DECBUFFER+2)]; + // buffer for temporary, b [must be same size as a] + decNumber bufb[D2N(DECBUFFER+2)]; + decNumber *allocbuff=NULL; // -> allocated buff, iff allocated + decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated + decNumber *allocbufb=NULL; // -> allocated bufb, iff allocated + decNumber *f=buff; // reduced fraction + decNumber *a=bufa; // approximation to result + decNumber *b=bufb; // intermediate result + // buffer for temporary variable, up to 3 digits + decNumber buft[D2N(3)]; + decNumber *t=buft; // up-to-3-digit constant or work + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operand and set lostDigits status, as needed + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, &status); + if (allocrhs==NULL) break; + // [Note: 'f' allocation below could reuse this buffer if + // used, but as this is rare they are kept separate for clarity.] + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + // handle infinities and NaNs + if (SPECIALARG) { + if (decNumberIsInfinite(rhs)) { // an infinity + if (decNumberIsNegative(rhs)) status|=DEC_Invalid_operation; + else decNumberCopy(res, rhs); // +Infinity + } + else decNaNs(res, rhs, NULL, set, &status); // a NaN + break; + } + + // calculate the ideal (preferred) exponent [floor(exp/2)] + // [It would be nicer to write: ideal=rhs->exponent>>1, but this + // generates a compiler warning. Generated code is the same.] + ideal=(rhs->exponent&~1)/2; // target + + // handle zeros + if (ISZERO(rhs)) { + decNumberCopy(res, rhs); // could be 0 or -0 + res->exponent=ideal; // use the ideal [safe] + // use decFinish to clamp any out-of-range exponent, etc. + decFinish(res, set, &residue, &status); + break; + } + + // any other -x is an oops + if (decNumberIsNegative(rhs)) { + status|=DEC_Invalid_operation; + break; + } + + // space is needed for three working variables + // f -- the same precision as the RHS, reduced to 0.01->0.99... + // a -- Hull's approximation -- precision, when assigned, is + // currentprecision+1 or the input argument precision, + // whichever is larger (+2 for use as temporary) + // b -- intermediate temporary result (same size as a) + // if any is too long for local storage, then allocate + workp=MAXI(set->digits+1, rhs->digits); // actual rounding precision + workp=MAXI(workp, 7); // at least 7 for low cases + maxp=workp+2; // largest working precision + + needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); + if (needbytes>(Int)sizeof(buff)) { + allocbuff=(decNumber *)malloc(needbytes); + if (allocbuff==NULL) { // hopeless -- abandon + status|=DEC_Insufficient_storage; + break;} + f=allocbuff; // use the allocated space + } + // a and b both need to be able to hold a maxp-length number + needbytes=sizeof(decNumber)+(D2U(maxp)-1)*sizeof(Unit); + if (needbytes>(Int)sizeof(bufa)) { // [same applies to b] + allocbufa=(decNumber *)malloc(needbytes); + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufa==NULL || allocbufb==NULL) { // hopeless + status|=DEC_Insufficient_storage; + break;} + a=allocbufa; // use the allocated spaces + b=allocbufb; // .. + } + + // copy rhs -> f, save exponent, and reduce so 0.1 <= f < 1 + decNumberCopy(f, rhs); + exp=f->exponent+f->digits; // adjusted to Hull rules + f->exponent=-(f->digits); // to range + + // set up working context + decContextDefault(&workset, DEC_INIT_DECIMAL64); + workset.emax=DEC_MAX_EMAX; + workset.emin=DEC_MIN_EMIN; + + // [Until further notice, no error is possible and status bits + // (Rounded, etc.) should be ignored, not accumulated.] + + // Calculate initial approximation, and allow for odd exponent + workset.digits=workp; // p for initial calculation + t->bits=0; t->digits=3; + a->bits=0; a->digits=3; + if ((exp & 1)==0) { // even exponent + // Set t=0.259, a=0.819 + t->exponent=-3; + a->exponent=-3; + #if DECDPUN>=3 + t->lsu[0]=259; + a->lsu[0]=819; + #elif DECDPUN==2 + t->lsu[0]=59; t->lsu[1]=2; + a->lsu[0]=19; a->lsu[1]=8; + #else + t->lsu[0]=9; t->lsu[1]=5; t->lsu[2]=2; + a->lsu[0]=9; a->lsu[1]=1; a->lsu[2]=8; + #endif + } + else { // odd exponent + // Set t=0.0819, a=2.59 + f->exponent--; // f=f/10 + exp++; // e=e+1 + t->exponent=-4; + a->exponent=-2; + #if DECDPUN>=3 + t->lsu[0]=819; + a->lsu[0]=259; + #elif DECDPUN==2 + t->lsu[0]=19; t->lsu[1]=8; + a->lsu[0]=59; a->lsu[1]=2; + #else + t->lsu[0]=9; t->lsu[1]=1; t->lsu[2]=8; + a->lsu[0]=9; a->lsu[1]=5; a->lsu[2]=2; + #endif + } + + decMultiplyOp(a, a, f, &workset, &ignore); // a=a*f + decAddOp(a, a, t, &workset, 0, &ignore); // ..+t + // [a is now the initial approximation for sqrt(f), calculated with + // currentprecision, which is also a's precision.] + + // the main calculation loop + decNumberZero(&dzero); // make 0 + decNumberZero(t); // set t = 0.5 + t->lsu[0]=5; // .. + t->exponent=-1; // .. + workset.digits=3; // initial p + for (; workset.digitsexponent+=exp/2; // set correct exponent + rstatus=0; // clear status + residue=0; // .. and accumulator + decCopyFit(a, a, &approxset, &residue, &rstatus); // reduce (if needed) + decFinish(a, &approxset, &residue, &rstatus); // clean and finalize + + // Overflow was possible if the input exponent was out-of-range, + // in which case quit + if (rstatus&DEC_Overflow) { + status=rstatus; // use the status as-is + decNumberCopy(res, a); // copy to result + break; + } + + // Preserve status except Inexact/Rounded + status|=(rstatus & ~(DEC_Rounded|DEC_Inexact)); + + // Carry out the Hull correction + a->exponent-=exp/2; // back to 0.1->1 + + // a is now at final precision and within 1 ulp of the properly + // rounded square root of f; to ensure proper rounding, compare + // squares of (a - l/2 ulp) and (a + l/2 ulp) with f. + // Here workset.digits=maxp and t=0.5, and a->digits determines + // the ulp + workset.digits--; // maxp-1 is OK now + t->exponent=-a->digits-1; // make 0.5 ulp + decAddOp(b, a, t, &workset, DECNEG, &ignore); // b = a - 0.5 ulp + workset.round=DEC_ROUND_UP; + decMultiplyOp(b, b, b, &workset, &ignore); // b = mulru(b, b) + decCompareOp(b, f, b, &workset, COMPARE, &ignore); // b ? f, reversed + if (decNumberIsNegative(b)) { // f < b [i.e., b > f] + // this is the more common adjustment, though both are rare + t->exponent++; // make 1.0 ulp + t->lsu[0]=1; // .. + decAddOp(a, a, t, &workset, DECNEG, &ignore); // a = a - 1 ulp + // assign to approx [round to length] + approxset.emin-=exp/2; // adjust to match a + approxset.emax-=exp/2; + decAddOp(a, &dzero, a, &approxset, 0, &ignore); + } + else { + decAddOp(b, a, t, &workset, 0, &ignore); // b = a + 0.5 ulp + workset.round=DEC_ROUND_DOWN; + decMultiplyOp(b, b, b, &workset, &ignore); // b = mulrd(b, b) + decCompareOp(b, b, f, &workset, COMPARE, &ignore); // b ? f + if (decNumberIsNegative(b)) { // b < f + t->exponent++; // make 1.0 ulp + t->lsu[0]=1; // .. + decAddOp(a, a, t, &workset, 0, &ignore); // a = a + 1 ulp + // assign to approx [round to length] + approxset.emin-=exp/2; // adjust to match a + approxset.emax-=exp/2; + decAddOp(a, &dzero, a, &approxset, 0, &ignore); + } + } + // [no errors are possible in the above, and rounding/inexact during + // estimation are irrelevant, so status was not accumulated] + + // Here, 0.1 <= a < 1 (still), so adjust back + a->exponent+=exp/2; // set correct exponent + + // count droppable zeros [after any subnormal rounding] by + // trimming a copy + decNumberCopy(b, a); + decTrim(b, set, 1, 1, &dropped); // [drops trailing zeros] + + // Set Inexact and Rounded. The answer can only be exact if + // it is short enough so that squaring it could fit in workp + // digits, so this is the only (relatively rare) condition that + // a careful check is needed + if (b->digits*2-1 > workp) { // cannot fit + status|=DEC_Inexact|DEC_Rounded; + } + else { // could be exact/unrounded + uInt mstatus=0; // local status + decMultiplyOp(b, b, b, &workset, &mstatus); // try the multiply + if (mstatus&DEC_Overflow) { // result just won't fit + status|=DEC_Inexact|DEC_Rounded; + } + else { // plausible + decCompareOp(t, b, rhs, &workset, COMPARE, &mstatus); // b ? rhs + if (!ISZERO(t)) status|=DEC_Inexact|DEC_Rounded; // not equal + else { // is Exact + // here, dropped is the count of trailing zeros in 'a' + // use closest exponent to ideal... + Int todrop=ideal-a->exponent; // most that can be dropped + if (todrop<0) status|=DEC_Rounded; // ideally would add 0s + else { // unrounded + // there are some to drop, but emax may not allow all + Int maxexp=set->emax-set->digits+1; + Int maxdrop=maxexp-a->exponent; + if (todrop>maxdrop && set->clamp) { // apply clamping + todrop=maxdrop; + status|=DEC_Clamped; + } + if (dropped0) { // have some to drop + decShiftToLeast(a->lsu, D2U(a->digits), todrop); + a->exponent+=todrop; // maintain numerical value + a->digits-=todrop; // new length + } + } + } + } + } + + // double-check Underflow, as perhaps the result could not have + // been subnormal (initial argument too big), or it is now Exact + if (status&DEC_Underflow) { + Int ae=rhs->exponent+rhs->digits-1; // adjusted exponent + // check if truly subnormal + #if DECEXTFLAG // DEC_Subnormal too + if (ae>=set->emin*2) status&=~(DEC_Subnormal|DEC_Underflow); + #else + if (ae>=set->emin*2) status&=~DEC_Underflow; + #endif + // check if truly inexact + if (!(status&DEC_Inexact)) status&=~DEC_Underflow; + } + + decNumberCopy(res, a); // a is now the result + } while(0); // end protected + + if (allocbuff!=NULL) free(allocbuff); // drop any storage used + if (allocbufa!=NULL) free(allocbufa); // .. + if (allocbufb!=NULL) free(allocbufb); // .. + #if DECSUBSET + if (allocrhs !=NULL) free(allocrhs); // .. + #endif + if (status!=0) decStatus(res, status, set);// then report status + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberSquareRoot + +/* ------------------------------------------------------------------ */ +/* decNumberSubtract -- subtract two Numbers */ +/* */ +/* This computes C = A - B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X-X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* */ +/* C must have space for set->digits digits. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberSubtract(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + uInt status=0; // accumulator + + decAddOp(res, lhs, rhs, set, DECNEG, &status); + if (status!=0) decStatus(res, status, set); + #if DECCHECK + decCheckInexact(res, set); + #endif + return res; + } // decNumberSubtract + +/* ------------------------------------------------------------------ */ +/* decNumberToIntegralExact -- round-to-integral-value with InExact */ +/* decNumberToIntegralValue -- round-to-integral-value */ +/* */ +/* res is the result */ +/* rhs is input number */ +/* set is the context */ +/* */ +/* res must have space for any value of rhs. */ +/* */ +/* This implements the IEEE special operators and therefore treats */ +/* special values as valid. For finite numbers it returns */ +/* rescale(rhs, 0) if rhs->exponent is <0. */ +/* Otherwise the result is rhs (so no error is possible, except for */ +/* sNaN). */ +/* */ +/* The context is used for rounding mode and status after sNaN, but */ +/* the digits setting is ignored. The Exact version will signal */ +/* Inexact if the result differs numerically from rhs; the other */ +/* never signals Inexact. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberToIntegralExact(decNumber *res, const decNumber *rhs, + decContext *set) { + decNumber dn; + decContext workset; // working context + uInt status=0; // accumulator + + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + // handle infinities and NaNs + if (SPECIALARG) { + if (decNumberIsInfinite(rhs)) decNumberCopy(res, rhs); // an Infinity + else decNaNs(res, rhs, NULL, set, &status); // a NaN + } + else { // finite + // have a finite number; no error possible (res must be big enough) + if (rhs->exponent>=0) return decNumberCopy(res, rhs); + // that was easy, but if negative exponent there is work to do... + workset=*set; // clone rounding, etc. + workset.digits=rhs->digits; // no length rounding + workset.traps=0; // no traps + decNumberZero(&dn); // make a number with exponent 0 + decNumberQuantize(res, rhs, &dn, &workset); + status|=workset.status; + } + if (status!=0) decStatus(res, status, set); + return res; + } // decNumberToIntegralExact + +decNumber * decNumberToIntegralValue(decNumber *res, const decNumber *rhs, + decContext *set) { + decContext workset=*set; // working context + workset.traps=0; // no traps + decNumberToIntegralExact(res, rhs, &workset); + // this never affects set, except for sNaNs; NaN will have been set + // or propagated already, so no need to call decStatus + set->status|=workset.status&DEC_Invalid_operation; + return res; + } // decNumberToIntegralValue + +/* ------------------------------------------------------------------ */ +/* decNumberXor -- XOR two Numbers, digitwise */ +/* */ +/* This computes C = A ^ B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X^X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context (used for result length and error report) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Logical function restrictions apply (see above); a NaN is */ +/* returned with Invalid_operation if a restriction is violated. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberXor(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + const Unit *ua, *ub; // -> operands + const Unit *msua, *msub; // -> operand msus + Unit *uc, *msuc; // -> result and its msu + Int msudigs; // digits in res msu + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + if (lhs->exponent!=0 || decNumberIsSpecial(lhs) || decNumberIsNegative(lhs) + || rhs->exponent!=0 || decNumberIsSpecial(rhs) || decNumberIsNegative(rhs)) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + // operands are valid + ua=lhs->lsu; // bottom-up + ub=rhs->lsu; // .. + uc=res->lsu; // .. + msua=ua+D2U(lhs->digits)-1; // -> msu of lhs + msub=ub+D2U(rhs->digits)-1; // -> msu of rhs + msuc=uc+D2U(set->digits)-1; // -> msu of result + msudigs=MSUDIGITS(set->digits); // [faster than remainder] + for (; uc<=msuc; ua++, ub++, uc++) { // Unit loop + Unit a, b; // extract units + if (ua>msua) a=0; + else a=*ua; + if (ub>msub) b=0; + else b=*ub; + *uc=0; // can now write back + if (a|b) { // maybe 1 bits to examine + Int i, j; + // This loop could be unrolled and/or use BIN2BCD tables + for (i=0; i1) { + decStatus(res, DEC_Invalid_operation, set); + return res; + } + if (uc==msuc && i==msudigs-1) break; // just did final digit + } // each digit + } // non-zero + } // each unit + // [here uc-1 is the msu of the result] + res->digits=decGetDigits(res->lsu, uc-res->lsu); + res->exponent=0; // integer + res->bits=0; // sign=0 + return res; // [no status to set] + } // decNumberXor + + +/* ================================================================== */ +/* Utility routines */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* decNumberClass -- return the decClass of a decNumber */ +/* dn -- the decNumber to test */ +/* set -- the context to use for Emin */ +/* returns the decClass enum */ +/* ------------------------------------------------------------------ */ +enum decClass decNumberClass(const decNumber *dn, decContext *set) { + if (decNumberIsSpecial(dn)) { + if (decNumberIsQNaN(dn)) return DEC_CLASS_QNAN; + if (decNumberIsSNaN(dn)) return DEC_CLASS_SNAN; + // must be an infinity + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_INF; + return DEC_CLASS_POS_INF; + } + // is finite + if (decNumberIsNormal(dn, set)) { // most common + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_NORMAL; + return DEC_CLASS_POS_NORMAL; + } + // is subnormal or zero + if (decNumberIsZero(dn)) { // most common + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_ZERO; + return DEC_CLASS_POS_ZERO; + } + if (decNumberIsNegative(dn)) return DEC_CLASS_NEG_SUBNORMAL; + return DEC_CLASS_POS_SUBNORMAL; + } // decNumberClass + +/* ------------------------------------------------------------------ */ +/* decNumberClassToString -- convert decClass to a string */ +/* */ +/* eclass is a valid decClass */ +/* returns a constant string describing the class (max 13+1 chars) */ +/* ------------------------------------------------------------------ */ +const char *decNumberClassToString(enum decClass eclass) { + if (eclass==DEC_CLASS_POS_NORMAL) return DEC_ClassString_PN; + if (eclass==DEC_CLASS_NEG_NORMAL) return DEC_ClassString_NN; + if (eclass==DEC_CLASS_POS_ZERO) return DEC_ClassString_PZ; + if (eclass==DEC_CLASS_NEG_ZERO) return DEC_ClassString_NZ; + if (eclass==DEC_CLASS_POS_SUBNORMAL) return DEC_ClassString_PS; + if (eclass==DEC_CLASS_NEG_SUBNORMAL) return DEC_ClassString_NS; + if (eclass==DEC_CLASS_POS_INF) return DEC_ClassString_PI; + if (eclass==DEC_CLASS_NEG_INF) return DEC_ClassString_NI; + if (eclass==DEC_CLASS_QNAN) return DEC_ClassString_QN; + if (eclass==DEC_CLASS_SNAN) return DEC_ClassString_SN; + return DEC_ClassString_UN; // Unknown + } // decNumberClassToString + +/* ------------------------------------------------------------------ */ +/* decNumberCopy -- copy a number */ +/* */ +/* dest is the target decNumber */ +/* src is the source decNumber */ +/* returns dest */ +/* */ +/* (dest==src is allowed and is a no-op) */ +/* All fields are updated as required. This is a utility operation, */ +/* so special values are unchanged and no error is possible. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCopy(decNumber *dest, const decNumber *src) { + + #if DECCHECK + if (src==NULL) return decNumberZero(dest); + #endif + + if (dest==src) return dest; // no copy required + + // Use explicit assignments here as structure assignment could copy + // more than just the lsu (for small DECDPUN). This would not affect + // the value of the results, but could disturb test harness spill + // checking. + dest->bits=src->bits; + dest->exponent=src->exponent; + dest->digits=src->digits; + dest->lsu[0]=src->lsu[0]; + if (src->digits>DECDPUN) { // more Units to come + const Unit *smsup, *s; // work + Unit *d; // .. + // memcpy for the remaining Units would be safe as they cannot + // overlap. However, this explicit loop is faster in short cases. + d=dest->lsu+1; // -> first destination + smsup=src->lsu+D2U(src->digits); // -> source msu+1 + for (s=src->lsu+1; sdigits digits. */ +/* No exception or error can occur; this is a quiet bitwise operation.*/ +/* See also decNumberAbs for a checking version of this. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCopyAbs(decNumber *res, const decNumber *rhs) { + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; + #endif + decNumberCopy(res, rhs); + res->bits&=~DECNEG; // turn off sign + return res; + } // decNumberCopyAbs + +/* ------------------------------------------------------------------ */ +/* decNumberCopyNegate -- quiet negate value operator */ +/* */ +/* This sets C = negate(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* */ +/* C must have space for set->digits digits. */ +/* No exception or error can occur; this is a quiet bitwise operation.*/ +/* See also decNumberMinus for a checking version of this. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCopyNegate(decNumber *res, const decNumber *rhs) { + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; + #endif + decNumberCopy(res, rhs); + res->bits^=DECNEG; // invert the sign + return res; + } // decNumberCopyNegate + +/* ------------------------------------------------------------------ */ +/* decNumberCopySign -- quiet copy and set sign operator */ +/* */ +/* This sets C = A with the sign of B */ +/* */ +/* res is C, the result. C may be A */ +/* lhs is A */ +/* rhs is B */ +/* */ +/* C must have space for set->digits digits. */ +/* No exception or error can occur; this is a quiet bitwise operation.*/ +/* ------------------------------------------------------------------ */ +decNumber * decNumberCopySign(decNumber *res, const decNumber *lhs, + const decNumber *rhs) { + uByte sign; // rhs sign + #if DECCHECK + if (decCheckOperands(res, DECUNUSED, rhs, DECUNCONT)) return res; + #endif + sign=rhs->bits & DECNEG; // save sign bit + decNumberCopy(res, lhs); + res->bits&=~DECNEG; // clear the sign + res->bits|=sign; // set from rhs + return res; + } // decNumberCopySign + +/* ------------------------------------------------------------------ */ +/* decNumberGetBCD -- get the coefficient in BCD8 */ +/* dn is the source decNumber */ +/* bcd is the uInt array that will receive dn->digits BCD bytes, */ +/* most-significant at offset 0 */ +/* returns bcd */ +/* */ +/* bcd must have at least dn->digits bytes. No error is possible; if */ +/* dn is a NaN or Infinite, digits must be 1 and the coefficient 0. */ +/* ------------------------------------------------------------------ */ +uByte * decNumberGetBCD(const decNumber *dn, uByte *bcd) { + uByte *ub=bcd+dn->digits-1; // -> lsd + const Unit *up=dn->lsu; // Unit pointer, -> lsu + + #if DECDPUN==1 // trivial simple copy + for (; ub>=bcd; ub--, up++) *ub=*up; + #else // chopping needed + uInt u=*up; // work + uInt cut=DECDPUN; // downcounter through unit + for (; ub>=bcd; ub--) { + *ub=(uByte)(u%10); // [*6554 trick inhibits, here] + u=u/10; + cut--; + if (cut>0) continue; // more in this unit + up++; + u=*up; + cut=DECDPUN; + } + #endif + return bcd; + } // decNumberGetBCD + +/* ------------------------------------------------------------------ */ +/* decNumberSetBCD -- set (replace) the coefficient from BCD8 */ +/* dn is the target decNumber */ +/* bcd is the uInt array that will source n BCD bytes, most- */ +/* significant at offset 0 */ +/* n is the number of digits in the source BCD array (bcd) */ +/* returns dn */ +/* */ +/* dn must have space for at least n digits. No error is possible; */ +/* if dn is a NaN, or Infinite, or is to become a zero, n must be 1 */ +/* and bcd[0] zero. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberSetBCD(decNumber *dn, const uByte *bcd, uInt n) { + Unit *up=dn->lsu+D2U(dn->digits)-1; // -> msu [target pointer] + const uByte *ub=bcd; // -> source msd + + #if DECDPUN==1 // trivial simple copy + for (; ub=dn->lsu; up--) { // each Unit from msu + *up=0; // will take <=DECDPUN digits + for (; cut>0; ub++, cut--) *up=X10(*up)+*ub; + cut=DECDPUN; // next Unit has all digits + } + #endif + dn->digits=n; // set digit count + return dn; + } // decNumberSetBCD + +/* ------------------------------------------------------------------ */ +/* decNumberIsNormal -- test normality of a decNumber */ +/* dn is the decNumber to test */ +/* set is the context to use for Emin */ +/* returns 1 if |dn| is finite and >=Nmin, 0 otherwise */ +/* ------------------------------------------------------------------ */ +Int decNumberIsNormal(const decNumber *dn, decContext *set) { + Int ae; // adjusted exponent + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, set)) return 0; + #endif + + if (decNumberIsSpecial(dn)) return 0; // not finite + if (decNumberIsZero(dn)) return 0; // not non-zero + + ae=dn->exponent+dn->digits-1; // adjusted exponent + if (aeemin) return 0; // is subnormal + return 1; + } // decNumberIsNormal + +/* ------------------------------------------------------------------ */ +/* decNumberIsSubnormal -- test subnormality of a decNumber */ +/* dn is the decNumber to test */ +/* set is the context to use for Emin */ +/* returns 1 if |dn| is finite, non-zero, and exponent+dn->digits-1; // adjusted exponent + if (aeemin) return 1; // is subnormal + return 0; + } // decNumberIsSubnormal + +/* ------------------------------------------------------------------ */ +/* decNumberTrim -- remove insignificant zeros */ +/* */ +/* dn is the number to trim */ +/* returns dn */ +/* */ +/* All fields are updated as required. This is a utility operation, */ +/* so special values are unchanged and no error is possible. The */ +/* zeros are removed unconditionally. */ +/* ------------------------------------------------------------------ */ +decNumber * decNumberTrim(decNumber *dn) { + Int dropped; // work + decContext set; // .. + #if DECCHECK + if (decCheckOperands(DECUNRESU, DECUNUSED, dn, DECUNCONT)) return dn; + #endif + decContextDefault(&set, DEC_INIT_BASE); // clamp=0 + return decTrim(dn, &set, 0, 1, &dropped); + } // decNumberTrim + +/* ------------------------------------------------------------------ */ +/* decNumberVersion -- return the name and version of this module */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +const char * decNumberVersion(void) { + return DECVERSION; + } // decNumberVersion + +/* ------------------------------------------------------------------ */ +/* decNumberZero -- set a number to 0 */ +/* */ +/* dn is the number to set, with space for one digit */ +/* returns dn */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +// Memset is not used as it is much slower in some environments. +decNumber * decNumberZero(decNumber *dn) { + + #if DECCHECK + if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn; + #endif + + dn->bits=0; + dn->exponent=0; + dn->digits=1; + dn->lsu[0]=0; + return dn; + } // decNumberZero + +/* ================================================================== */ +/* Local routines */ +/* ================================================================== */ + +/* ------------------------------------------------------------------ */ +/* decToString -- lay out a number into a string */ +/* */ +/* dn is the number to lay out */ +/* string is where to lay out the number */ +/* eng is 1 if Engineering, 0 if Scientific */ +/* */ +/* string must be at least dn->digits+14 characters long */ +/* No error is possible. */ +/* */ +/* Note that this routine can generate a -0 or 0.000. These are */ +/* never generated in subset to-number or arithmetic, but can occur */ +/* in non-subset arithmetic (e.g., -1*0 or 1.234-1.234). */ +/* ------------------------------------------------------------------ */ +// If DECCHECK is enabled the string "?" is returned if a number is +// invalid. +static void decToString(const decNumber *dn, char *string, Flag eng) { + Int exp=dn->exponent; // local copy + Int e; // E-part value + Int pre; // digits before the '.' + Int cut; // for counting digits in a Unit + char *c=string; // work [output pointer] + const Unit *up=dn->lsu+D2U(dn->digits)-1; // -> msu [input pointer] + uInt u, pow; // work + + #if DECCHECK + if (decCheckOperands(DECUNRESU, dn, DECUNUSED, DECUNCONT)) { + strcpy(string, "?"); + return;} + #endif + + if (decNumberIsNegative(dn)) { // Negatives get a minus + *c='-'; + c++; + } + if (dn->bits&DECSPECIAL) { // Is a special value + if (decNumberIsInfinite(dn)) { + strcpy(c, "Inf"); + strcpy(c+3, "inity"); + return;} + // a NaN + if (dn->bits&DECSNAN) { // signalling NaN + *c='s'; + c++; + } + strcpy(c, "NaN"); + c+=3; // step past + // if not a clean non-zero coefficient, that's all there is in a + // NaN string + if (exp!=0 || (*dn->lsu==0 && dn->digits==1)) return; + // [drop through to add integer] + } + + // calculate how many digits in msu, and hence first cut + cut=MSUDIGITS(dn->digits); // [faster than remainder] + cut--; // power of ten for digit + + if (exp==0) { // simple integer [common fastpath] + for (;up>=dn->lsu; up--) { // each Unit from msu + u=*up; // contains DECDPUN digits to lay out + for (; cut>=0; c++, cut--) TODIGIT(u, cut, c, pow); + cut=DECDPUN-1; // next Unit has all digits + } + *c='\0'; // terminate the string + return;} + + /* non-0 exponent -- assume plain form */ + pre=dn->digits+exp; // digits before '.' + e=0; // no E + if ((exp>0) || (pre<-5)) { // need exponential form + e=exp+dn->digits-1; // calculate E value + pre=1; // assume one digit before '.' + if (eng && (e!=0)) { // engineering: may need to adjust + Int adj; // adjustment + // The C remainder operator is undefined for negative numbers, so + // a positive remainder calculation must be used here + if (e<0) { + adj=(-e)%3; + if (adj!=0) adj=3-adj; + } + else { // e>0 + adj=e%3; + } + e=e-adj; + // if dealing with zero still produce an exponent which is a + // multiple of three, as expected, but there will only be the + // one zero before the E, still. Otherwise note the padding. + if (!ISZERO(dn)) pre+=adj; + else { // is zero + if (adj!=0) { // 0.00Esnn needed + e=e+3; + pre=-(2-adj); + } + } // zero + } // eng + } // need exponent + + /* lay out the digits of the coefficient, adding 0s and . as needed */ + u=*up; + if (pre>0) { // xxx.xxx or xx00 (engineering) form + Int n=pre; + for (; pre>0; pre--, c++, cut--) { + if (cut<0) { // need new Unit + if (up==dn->lsu) break; // out of input digits (pre>digits) + up--; + cut=DECDPUN-1; + u=*up; + } + TODIGIT(u, cut, c, pow); + } + if (ndigits) { // more to come, after '.' + *c='.'; c++; + for (;; c++, cut--) { + if (cut<0) { // need new Unit + if (up==dn->lsu) break; // out of input digits + up--; + cut=DECDPUN-1; + u=*up; + } + TODIGIT(u, cut, c, pow); + } + } + else for (; pre>0; pre--, c++) *c='0'; // 0 padding (for engineering) needed + } + else { // 0.xxx or 0.000xxx form + *c='0'; c++; + *c='.'; c++; + for (; pre<0; pre++, c++) *c='0'; // add any 0's after '.' + for (; ; c++, cut--) { + if (cut<0) { // need new Unit + if (up==dn->lsu) break; // out of input digits + up--; + cut=DECDPUN-1; + u=*up; + } + TODIGIT(u, cut, c, pow); + } + } + + /* Finally add the E-part, if needed. It will never be 0, has a + base maximum and minimum of +999999999 through -999999999, but + could range down to -1999999998 for anormal numbers */ + if (e!=0) { + Flag had=0; // 1=had non-zero + *c='E'; c++; + *c='+'; c++; // assume positive + u=e; // .. + if (e<0) { + *(c-1)='-'; // oops, need - + u=-e; // uInt, please + } + // lay out the exponent [_itoa or equivalent is not ANSI C] + for (cut=9; cut>=0; cut--) { + TODIGIT(u, cut, c, pow); + if (*c=='0' && !had) continue; // skip leading zeros + had=1; // had non-0 + c++; // step for next + } // cut + } + *c='\0'; // terminate the string (all paths) + return; + } // decToString + +/* ------------------------------------------------------------------ */ +/* decAddOp -- add/subtract operation */ +/* */ +/* This computes C = A + B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X+X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* negate is DECNEG if rhs should be negated, or 0 otherwise */ +/* status accumulates status for the caller */ +/* */ +/* C must have space for set->digits digits. */ +/* Inexact in status must be 0 for correct Exact zero sign in result */ +/* ------------------------------------------------------------------ */ +/* If possible, the coefficient is calculated directly into C. */ +/* However, if: */ +/* -- a digits+1 calculation is needed because the numbers are */ +/* unaligned and span more than set->digits digits */ +/* -- a carry to digits+1 digits looks possible */ +/* -- C is the same as A or B, and the result would destructively */ +/* overlap the A or B coefficient */ +/* then the result must be calculated into a temporary buffer. In */ +/* this case a local (stack) buffer is used if possible, and only if */ +/* too long for that does malloc become the final resort. */ +/* */ +/* Misalignment is handled as follows: */ +/* Apad: (AExp>BExp) Swap operands and proceed as for BExp>AExp. */ +/* BPad: Apply the padding by a combination of shifting (whole */ +/* units) and multiplication (part units). */ +/* */ +/* Addition, especially x=x+1, is speed-critical. */ +/* The static buffer is larger than might be expected to allow for */ +/* calls from higher-level funtions (notable exp). */ +/* ------------------------------------------------------------------ */ +static decNumber * decAddOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + uByte negate, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated + decNumber *allocrhs=NULL; // .., rhs + #endif + Int rhsshift; // working shift (in Units) + Int maxdigits; // longest logical length + Int mult; // multiplier + Int residue; // rounding accumulator + uByte bits; // result bits + Flag diffsign; // non-0 if arguments have different sign + Unit *acc; // accumulator for result + Unit accbuff[SD2U(DECBUFFER*2+20)]; // local buffer [*2+20 reduces many + // allocations when called from + // other operations, notable exp] + Unit *allocacc=NULL; // -> allocated acc buffer, iff allocated + Int reqdigits=set->digits; // local copy; requested DIGITS + Int padding; // work + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operands and set lostDigits status, as needed + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==NULL) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + // note whether signs differ [used all paths] + diffsign=(Flag)((lhs->bits^rhs->bits^negate)&DECNEG); + + // handle infinities and NaNs + if (SPECIALARGS) { // a special bit set + if (SPECIALARGS & (DECSNAN | DECNAN)) // a NaN + decNaNs(res, lhs, rhs, set, status); + else { // one or two infinities + if (decNumberIsInfinite(lhs)) { // LHS is infinity + // two infinities with different signs is invalid + if (decNumberIsInfinite(rhs) && diffsign) { + *status|=DEC_Invalid_operation; + break; + } + bits=lhs->bits & DECNEG; // get sign from LHS + } + else bits=(rhs->bits^negate) & DECNEG;// RHS must be Infinity + bits|=DECINF; + decNumberZero(res); + res->bits=bits; // set +/- infinity + } // an infinity + break; + } + + // Quick exit for add 0s; return the non-0, modified as need be + if (ISZERO(lhs)) { + Int adjust; // work + Int lexp=lhs->exponent; // save in case LHS==RES + bits=lhs->bits; // .. + residue=0; // clear accumulator + decCopyFit(res, rhs, set, &residue, status); // copy (as needed) + res->bits^=negate; // flip if rhs was negated + #if DECSUBSET + if (set->extended) { // exponents on zeros count + #endif + // exponent will be the lower of the two + adjust=lexp-res->exponent; // adjustment needed [if -ve] + if (ISZERO(res)) { // both 0: special IEEE 754 rules + if (adjust<0) res->exponent=lexp; // set exponent + // 0-0 gives +0 unless rounding to -infinity, and -0-0 gives -0 + if (diffsign) { + if (set->round!=DEC_ROUND_FLOOR) res->bits=0; + else res->bits=DECNEG; // preserve 0 sign + } + } + else { // non-0 res + if (adjust<0) { // 0-padding needed + if ((res->digits-adjust)>set->digits) { + adjust=res->digits-set->digits; // to fit exactly + *status|=DEC_Rounded; // [but exact] + } + res->digits=decShiftToMost(res->lsu, res->digits, -adjust); + res->exponent+=adjust; // set the exponent. + } + } // non-0 res + #if DECSUBSET + } // extended + #endif + decFinish(res, set, &residue, status); // clean and finalize + break;} + + if (ISZERO(rhs)) { // [lhs is non-zero] + Int adjust; // work + Int rexp=rhs->exponent; // save in case RHS==RES + bits=rhs->bits; // be clean + residue=0; // clear accumulator + decCopyFit(res, lhs, set, &residue, status); // copy (as needed) + #if DECSUBSET + if (set->extended) { // exponents on zeros count + #endif + // exponent will be the lower of the two + // [0-0 case handled above] + adjust=rexp-res->exponent; // adjustment needed [if -ve] + if (adjust<0) { // 0-padding needed + if ((res->digits-adjust)>set->digits) { + adjust=res->digits-set->digits; // to fit exactly + *status|=DEC_Rounded; // [but exact] + } + res->digits=decShiftToMost(res->lsu, res->digits, -adjust); + res->exponent+=adjust; // set the exponent. + } + #if DECSUBSET + } // extended + #endif + decFinish(res, set, &residue, status); // clean and finalize + break;} + + // [NB: both fastpath and mainpath code below assume these cases + // (notably 0-0) have already been handled] + + // calculate the padding needed to align the operands + padding=rhs->exponent-lhs->exponent; + + // Fastpath cases where the numbers are aligned and normal, the RHS + // is all in one unit, no operand rounding is needed, and no carry, + // lengthening, or borrow is needed + if (padding==0 + && rhs->digits<=DECDPUN + && rhs->exponent>=set->emin // [some normals drop through] + && rhs->exponent<=set->emax-set->digits+1 // [could clamp] + && rhs->digits<=reqdigits + && lhs->digits<=reqdigits) { + Int partial=*lhs->lsu; + if (!diffsign) { // adding + partial+=*rhs->lsu; + if ((partial<=DECDPUNMAX) // result fits in unit + && (lhs->digits>=DECDPUN || // .. and no digits-count change + partial<(Int)powers[lhs->digits])) { // .. + if (res!=lhs) decNumberCopy(res, lhs); // not in place + *res->lsu=(Unit)partial; // [copy could have overwritten RHS] + break; + } + // else drop out for careful add + } + else { // signs differ + partial-=*rhs->lsu; + if (partial>0) { // no borrow needed, and non-0 result + if (res!=lhs) decNumberCopy(res, lhs); // not in place + *res->lsu=(Unit)partial; + // this could have reduced digits [but result>0] + res->digits=decGetDigits(res->lsu, D2U(res->digits)); + break; + } + // else drop out for careful subtract + } + } + + // Now align (pad) the lhs or rhs so they can be added or + // subtracted, as necessary. If one number is much larger than + // the other (that is, if in plain form there is a least one + // digit between the lowest digit of one and the highest of the + // other) padding with up to DIGITS-1 trailing zeros may be + // needed; then apply rounding (as exotic rounding modes may be + // affected by the residue). + rhsshift=0; // rhs shift to left (padding) in Units + bits=lhs->bits; // assume sign is that of LHS + mult=1; // likely multiplier + + // [if padding==0 the operands are aligned; no padding is needed] + if (padding!=0) { + // some padding needed; always pad the RHS, as any required + // padding can then be effected by a simple combination of + // shifts and a multiply + Flag swapped=0; + if (padding<0) { // LHS needs the padding + const decNumber *t; + padding=-padding; // will be +ve + bits=(uByte)(rhs->bits^negate); // assumed sign is now that of RHS + t=lhs; lhs=rhs; rhs=t; + swapped=1; + } + + // If, after pad, rhs would be longer than lhs by digits+1 or + // more then lhs cannot affect the answer, except as a residue, + // so only need to pad up to a length of DIGITS+1. + if (rhs->digits+padding > lhs->digits+reqdigits+1) { + // The RHS is sufficient + // for residue use the relative sign indication... + Int shift=reqdigits-rhs->digits; // left shift needed + residue=1; // residue for rounding + if (diffsign) residue=-residue; // signs differ + // copy, shortening if necessary + decCopyFit(res, rhs, set, &residue, status); + // if it was already shorter, then need to pad with zeros + if (shift>0) { + res->digits=decShiftToMost(res->lsu, res->digits, shift); + res->exponent-=shift; // adjust the exponent. + } + // flip the result sign if unswapped and rhs was negated + if (!swapped) res->bits^=negate; + decFinish(res, set, &residue, status); // done + break;} + + // LHS digits may affect result + rhsshift=D2U(padding+1)-1; // this much by Unit shift .. + mult=powers[padding-(rhsshift*DECDPUN)]; // .. this by multiplication + } // padding needed + + if (diffsign) mult=-mult; // signs differ + + // determine the longer operand + maxdigits=rhs->digits+padding; // virtual length of RHS + if (lhs->digits>maxdigits) maxdigits=lhs->digits; + + // Decide on the result buffer to use; if possible place directly + // into result. + acc=res->lsu; // assume add direct to result + // If destructive overlap, or the number is too long, or a carry or + // borrow to DIGITS+1 might be possible, a buffer must be used. + // [Might be worth more sophisticated tests when maxdigits==reqdigits] + if ((maxdigits>=reqdigits) // is, or could be, too large + || (res==rhs && rhsshift>0)) { // destructive overlap + // buffer needed, choose it; units for maxdigits digits will be + // needed, +1 Unit for carry or borrow + Int need=D2U(maxdigits)+1; + acc=accbuff; // assume use local buffer + if (need*sizeof(Unit)>sizeof(accbuff)) { + // printf("malloc add %ld %ld\n", need, sizeof(accbuff)); + allocacc=(Unit *)malloc(need*sizeof(Unit)); + if (allocacc==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + acc=allocacc; + } + } + + res->bits=(uByte)(bits&DECNEG); // it's now safe to overwrite.. + res->exponent=lhs->exponent; // .. operands (even if aliased) + + #if DECTRACE + decDumpAr('A', lhs->lsu, D2U(lhs->digits)); + decDumpAr('B', rhs->lsu, D2U(rhs->digits)); + printf(" :h: %ld %ld\n", rhsshift, mult); + #endif + + // add [A+B*m] or subtract [A+B*(-m)] + res->digits=decUnitAddSub(lhs->lsu, D2U(lhs->digits), + rhs->lsu, D2U(rhs->digits), + rhsshift, acc, mult) + *DECDPUN; // [units -> digits] + if (res->digits<0) { // borrowed... + res->digits=-res->digits; + res->bits^=DECNEG; // flip the sign + } + #if DECTRACE + decDumpAr('+', acc, D2U(res->digits)); + #endif + + // If a buffer was used the result must be copied back, possibly + // shortening. (If no buffer was used then the result must have + // fit, so can't need rounding and residue must be 0.) + residue=0; // clear accumulator + if (acc!=res->lsu) { + #if DECSUBSET + if (set->extended) { // round from first significant digit + #endif + // remove leading zeros that were added due to rounding up to + // integral Units -- before the test for rounding. + if (res->digits>reqdigits) + res->digits=decGetDigits(acc, D2U(res->digits)); + decSetCoeff(res, set, acc, res->digits, &residue, status); + #if DECSUBSET + } + else { // subset arithmetic rounds from original significant digit + // May have an underestimate. This only occurs when both + // numbers fit in DECDPUN digits and are padding with a + // negative multiple (-10, -100...) and the top digit(s) become + // 0. (This only matters when using X3.274 rules where the + // leading zero could be included in the rounding.) + if (res->digitsdigits))=0; // ensure leading 0 is there + res->digits=maxdigits; + } + else { + // remove leading zeros that added due to rounding up to + // integral Units (but only those in excess of the original + // maxdigits length, unless extended) before test for rounding. + if (res->digits>reqdigits) { + res->digits=decGetDigits(acc, D2U(res->digits)); + if (res->digitsdigits=maxdigits; + } + } + decSetCoeff(res, set, acc, res->digits, &residue, status); + // Now apply rounding if needed before removing leading zeros. + // This is safe because subnormals are not a possibility + if (residue!=0) { + decApplyRound(res, set, residue, status); + residue=0; // did what needed to be done + } + } // subset + #endif + } // used buffer + + // strip leading zeros [these were left on in case of subset subtract] + res->digits=decGetDigits(res->lsu, D2U(res->digits)); + + // apply checks and rounding + decFinish(res, set, &residue, status); + + // "When the sum of two operands with opposite signs is exactly + // zero, the sign of that sum shall be '+' in all rounding modes + // except round toward -Infinity, in which mode that sign shall be + // '-'." [Subset zeros also never have '-', set by decFinish.] + if (ISZERO(res) && diffsign + #if DECSUBSET + && set->extended + #endif + && (*status&DEC_Inexact)==0) { + if (set->round==DEC_ROUND_FLOOR) res->bits|=DECNEG; // sign - + else res->bits&=~DECNEG; // sign + + } + } while(0); // end protected + + if (allocacc!=NULL) free(allocacc); // drop any storage used + #if DECSUBSET + if (allocrhs!=NULL) free(allocrhs); // .. + if (alloclhs!=NULL) free(alloclhs); // .. + #endif + return res; + } // decAddOp + +/* ------------------------------------------------------------------ */ +/* decDivideOp -- division operation */ +/* */ +/* This routine performs the calculations for all four division */ +/* operators (divide, divideInteger, remainder, remainderNear). */ +/* */ +/* C=A op B */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X/X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* op is DIVIDE, DIVIDEINT, REMAINDER, or REMNEAR respectively. */ +/* status is the usual accumulator */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* ------------------------------------------------------------------ */ +/* The underlying algorithm of this routine is the same as in the */ +/* 1981 S/370 implementation, that is, non-restoring long division */ +/* with bi-unit (rather than bi-digit) estimation for each unit */ +/* multiplier. In this pseudocode overview, complications for the */ +/* Remainder operators and division residues for exact rounding are */ +/* omitted for clarity. */ +/* */ +/* Prepare operands and handle special values */ +/* Test for x/0 and then 0/x */ +/* Exp =Exp1 - Exp2 */ +/* Exp =Exp +len(var1) -len(var2) */ +/* Sign=Sign1 * Sign2 */ +/* Pad accumulator (Var1) to double-length with 0's (pad1) */ +/* Pad Var2 to same length as Var1 */ +/* msu2pair/plus=1st 2 or 1 units of var2, +1 to allow for round */ +/* have=0 */ +/* Do until (have=digits+1 OR residue=0) */ +/* if exp<0 then if integer divide/residue then leave */ +/* this_unit=0 */ +/* Do forever */ +/* compare numbers */ +/* if <0 then leave inner_loop */ +/* if =0 then (* quick exit without subtract *) do */ +/* this_unit=this_unit+1; output this_unit */ +/* leave outer_loop; end */ +/* Compare lengths of numbers (mantissae): */ +/* If same then tops2=msu2pair -- {units 1&2 of var2} */ +/* else tops2=msu2plus -- {0, unit 1 of var2} */ +/* tops1=first_unit_of_Var1*10**DECDPUN +second_unit_of_var1 */ +/* mult=tops1/tops2 -- Good and safe guess at divisor */ +/* if mult=0 then mult=1 */ +/* this_unit=this_unit+mult */ +/* subtract */ +/* end inner_loop */ +/* if have\=0 | this_unit\=0 then do */ +/* output this_unit */ +/* have=have+1; end */ +/* var2=var2/10 */ +/* exp=exp-1 */ +/* end outer_loop */ +/* exp=exp+1 -- set the proper exponent */ +/* if have=0 then generate answer=0 */ +/* Return (Result is defined by Var1) */ +/* */ +/* ------------------------------------------------------------------ */ +/* Two working buffers are needed during the division; one (digits+ */ +/* 1) to accumulate the result, and the other (up to 2*digits+1) for */ +/* long subtractions. These are acc and var1 respectively. */ +/* var1 is a copy of the lhs coefficient, var2 is the rhs coefficient.*/ +/* The static buffers may be larger than might be expected to allow */ +/* for calls from higher-level funtions (notable exp). */ +/* ------------------------------------------------------------------ */ +static decNumber * decDivideOp(decNumber *res, + const decNumber *lhs, const decNumber *rhs, + decContext *set, Flag op, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated + decNumber *allocrhs=NULL; // .., rhs + #endif + Unit accbuff[SD2U(DECBUFFER+DECDPUN+10)]; // local buffer + Unit *acc=accbuff; // -> accumulator array for result + Unit *allocacc=NULL; // -> allocated buffer, iff allocated + Unit *accnext; // -> where next digit will go + Int acclength; // length of acc needed [Units] + Int accunits; // count of units accumulated + Int accdigits; // count of digits accumulated + + Unit varbuff[SD2U(DECBUFFER*2+DECDPUN)]; // buffer for var1 + Unit *var1=varbuff; // -> var1 array for long subtraction + Unit *varalloc=NULL; // -> allocated buffer, iff used + Unit *msu1; // -> msu of var1 + + const Unit *var2; // -> var2 array + const Unit *msu2; // -> msu of var2 + Int msu2plus; // msu2 plus one [does not vary] + eInt msu2pair; // msu2 pair plus one [does not vary] + + Int var1units, var2units; // actual lengths + Int var2ulen; // logical length (units) + Int var1initpad=0; // var1 initial padding (digits) + Int maxdigits; // longest LHS or required acc length + Int mult; // multiplier for subtraction + Unit thisunit; // current unit being accumulated + Int residue; // for rounding + Int reqdigits=set->digits; // requested DIGITS + Int exponent; // working exponent + Int maxexponent=0; // DIVIDE maximum exponent if unrounded + uByte bits; // working sign + Unit *target; // work + const Unit *source; // .. + uInt const *pow; // .. + Int shift, cut; // .. + #if DECSUBSET + Int dropped; // work + #endif + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operands and set lostDigits status, as needed + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==NULL) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + bits=(lhs->bits^rhs->bits)&DECNEG; // assumed sign for divisions + + // handle infinities and NaNs + if (SPECIALARGS) { // a special bit set + if (SPECIALARGS & (DECSNAN | DECNAN)) { // one or two NaNs + decNaNs(res, lhs, rhs, set, status); + break; + } + // one or two infinities + if (decNumberIsInfinite(lhs)) { // LHS (dividend) is infinite + if (decNumberIsInfinite(rhs) || // two infinities are invalid .. + op & (REMAINDER | REMNEAR)) { // as is remainder of infinity + *status|=DEC_Invalid_operation; + break; + } + // [Note that infinity/0 raises no exceptions] + decNumberZero(res); + res->bits=bits|DECINF; // set +/- infinity + break; + } + else { // RHS (divisor) is infinite + residue=0; + if (op&(REMAINDER|REMNEAR)) { + // result is [finished clone of] lhs + decCopyFit(res, lhs, set, &residue, status); + } + else { // a division + decNumberZero(res); + res->bits=bits; // set +/- zero + // for DIVIDEINT the exponent is always 0. For DIVIDE, result + // is a 0 with infinitely negative exponent, clamped to minimum + if (op&DIVIDE) { + res->exponent=set->emin-set->digits+1; + *status|=DEC_Clamped; + } + } + decFinish(res, set, &residue, status); + break; + } + } + + // handle 0 rhs (x/0) + if (ISZERO(rhs)) { // x/0 is always exceptional + if (ISZERO(lhs)) { + decNumberZero(res); // [after lhs test] + *status|=DEC_Division_undefined;// 0/0 will become NaN + } + else { + decNumberZero(res); + if (op&(REMAINDER|REMNEAR)) *status|=DEC_Invalid_operation; + else { + *status|=DEC_Division_by_zero; // x/0 + res->bits=bits|DECINF; // .. is +/- Infinity + } + } + break;} + + // handle 0 lhs (0/x) + if (ISZERO(lhs)) { // 0/x [x!=0] + #if DECSUBSET + if (!set->extended) decNumberZero(res); + else { + #endif + if (op&DIVIDE) { + residue=0; + exponent=lhs->exponent-rhs->exponent; // ideal exponent + decNumberCopy(res, lhs); // [zeros always fit] + res->bits=bits; // sign as computed + res->exponent=exponent; // exponent, too + decFinalize(res, set, &residue, status); // check exponent + } + else if (op&DIVIDEINT) { + decNumberZero(res); // integer 0 + res->bits=bits; // sign as computed + } + else { // a remainder + exponent=rhs->exponent; // [save in case overwrite] + decNumberCopy(res, lhs); // [zeros always fit] + if (exponentexponent) res->exponent=exponent; // use lower + } + #if DECSUBSET + } + #endif + break;} + + // Precalculate exponent. This starts off adjusted (and hence fits + // in 31 bits) and becomes the usual unadjusted exponent as the + // division proceeds. The order of evaluation is important, here, + // to avoid wrap. + exponent=(lhs->exponent+lhs->digits)-(rhs->exponent+rhs->digits); + + // If the working exponent is -ve, then some quick exits are + // possible because the quotient is known to be <1 + // [for REMNEAR, it needs to be < -1, as -0.5 could need work] + if (exponent<0 && !(op==DIVIDE)) { + if (op&DIVIDEINT) { + decNumberZero(res); // integer part is 0 + #if DECSUBSET + if (set->extended) + #endif + res->bits=bits; // set +/- zero + break;} + // fastpath remainders so long as the lhs has the smaller + // (or equal) exponent + if (lhs->exponent<=rhs->exponent) { + if (op&REMAINDER || exponent<-1) { + // It is REMAINDER or safe REMNEAR; result is [finished + // clone of] lhs (r = x - 0*y) + residue=0; + decCopyFit(res, lhs, set, &residue, status); + decFinish(res, set, &residue, status); + break; + } + // [unsafe REMNEAR drops through] + } + } // fastpaths + + /* Long (slow) division is needed; roll up the sleeves... */ + + // The accumulator will hold the quotient of the division. + // If it needs to be too long for stack storage, then allocate. + acclength=D2U(reqdigits+DECDPUN); // in Units + if (acclength*sizeof(Unit)>sizeof(accbuff)) { + // printf("malloc dvacc %ld units\n", acclength); + allocacc=(Unit *)malloc(acclength*sizeof(Unit)); + if (allocacc==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + acc=allocacc; // use the allocated space + } + + // var1 is the padded LHS ready for subtractions. + // If it needs to be too long for stack storage, then allocate. + // The maximum units needed for var1 (long subtraction) is: + // Enough for + // (rhs->digits+reqdigits-1) -- to allow full slide to right + // or (lhs->digits) -- to allow for long lhs + // whichever is larger + // +1 -- for rounding of slide to right + // +1 -- for leading 0s + // +1 -- for pre-adjust if a remainder or DIVIDEINT + // [Note: unused units do not participate in decUnitAddSub data] + maxdigits=rhs->digits+reqdigits-1; + if (lhs->digits>maxdigits) maxdigits=lhs->digits; + var1units=D2U(maxdigits)+2; + // allocate a guard unit above msu1 for REMAINDERNEAR + if (!(op&DIVIDE)) var1units++; + if ((var1units+1)*sizeof(Unit)>sizeof(varbuff)) { + // printf("malloc dvvar %ld units\n", var1units+1); + varalloc=(Unit *)malloc((var1units+1)*sizeof(Unit)); + if (varalloc==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + var1=varalloc; // use the allocated space + } + + // Extend the lhs and rhs to full long subtraction length. The lhs + // is truly extended into the var1 buffer, with 0 padding, so a + // subtract in place is always possible. The rhs (var2) has + // virtual padding (implemented by decUnitAddSub). + // One guard unit was allocated above msu1 for rem=rem+rem in + // REMAINDERNEAR. + msu1=var1+var1units-1; // msu of var1 + source=lhs->lsu+D2U(lhs->digits)-1; // msu of input array + for (target=msu1; source>=lhs->lsu; source--, target--) *target=*source; + for (; target>=var1; target--) *target=0; + + // rhs (var2) is left-aligned with var1 at the start + var2ulen=var1units; // rhs logical length (units) + var2units=D2U(rhs->digits); // rhs actual length (units) + var2=rhs->lsu; // -> rhs array + msu2=var2+var2units-1; // -> msu of var2 [never changes] + // now set up the variables which will be used for estimating the + // multiplication factor. If these variables are not exact, add + // 1 to make sure that the multiplier is never overestimated. + msu2plus=*msu2; // it's value .. + if (var2units>1) msu2plus++; // .. +1 if any more + msu2pair=(eInt)*msu2*(DECDPUNMAX+1);// top two pair .. + if (var2units>1) { // .. [else treat 2nd as 0] + msu2pair+=*(msu2-1); // .. + if (var2units>2) msu2pair++; // .. +1 if any more + } + + // The calculation is working in units, which may have leading zeros, + // but the exponent was calculated on the assumption that they are + // both left-aligned. Adjust the exponent to compensate: add the + // number of leading zeros in var1 msu and subtract those in var2 msu. + // [This is actually done by counting the digits and negating, as + // lead1=DECDPUN-digits1, and similarly for lead2.] + for (pow=&powers[1]; *msu1>=*pow; pow++) exponent--; + for (pow=&powers[1]; *msu2>=*pow; pow++) exponent++; + + // Now, if doing an integer divide or remainder, ensure that + // the result will be Unit-aligned. To do this, shift the var1 + // accumulator towards least if need be. (It's much easier to + // do this now than to reassemble the residue afterwards, if + // doing a remainder.) Also ensure the exponent is not negative. + if (!(op&DIVIDE)) { + Unit *u; // work + // save the initial 'false' padding of var1, in digits + var1initpad=(var1units-D2U(lhs->digits))*DECDPUN; + // Determine the shift to do. + if (exponent<0) cut=-exponent; + else cut=DECDPUN-exponent%DECDPUN; + decShiftToLeast(var1, var1units, cut); + exponent+=cut; // maintain numerical value + var1initpad-=cut; // .. and reduce padding + // clean any most-significant units which were just emptied + for (u=msu1; cut>=DECDPUN; cut-=DECDPUN, u--) *u=0; + } // align + else { // is DIVIDE + maxexponent=lhs->exponent-rhs->exponent; // save + // optimization: if the first iteration will just produce 0, + // preadjust to skip it [valid for DIVIDE only] + if (*msu1<*msu2) { + var2ulen--; // shift down + exponent-=DECDPUN; // update the exponent + } + } + + // ---- start the long-division loops ------------------------------ + accunits=0; // no units accumulated yet + accdigits=0; // .. or digits + accnext=acc+acclength-1; // -> msu of acc [NB: allows digits+1] + for (;;) { // outer forever loop + thisunit=0; // current unit assumed 0 + // find the next unit + for (;;) { // inner forever loop + // strip leading zero units [from either pre-adjust or from + // subtract last time around]. Leave at least one unit. + for (; *msu1==0 && msu1>var1; msu1--) var1units--; + + if (var1units msu + for (pv1=msu1; ; pv1--, pv2--) { + // v1=*pv1 -- always OK + v2=0; // assume in padding + if (pv2>=var2) v2=*pv2; // in range + if (*pv1!=v2) break; // no longer the same + if (pv1==var1) break; // done; leave pv1 as is + } + // here when all inspected or a difference seen + if (*pv1v2. Prepare for real subtraction; the lengths are equal + // Estimate the multiplier (there's always a msu1-1)... + // Bring in two units of var2 to provide a good estimate. + mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2pair); + } // lengths the same + else { // var1units > var2ulen, so subtraction is safe + // The var2 msu is one unit towards the lsu of the var1 msu, + // so only one unit for var2 can be used. + mult=(Int)(((eInt)*msu1*(DECDPUNMAX+1)+*(msu1-1))/msu2plus); + } + if (mult==0) mult=1; // must always be at least 1 + // subtraction needed; var1 is > var2 + thisunit=(Unit)(thisunit+mult); // accumulate + // subtract var1-var2, into var1; only the overlap needs + // processing, as this is an in-place calculation + shift=var2ulen-var2units; + #if DECTRACE + decDumpAr('1', &var1[shift], var1units-shift); + decDumpAr('2', var2, var2units); + printf("m=%ld\n", -mult); + #endif + decUnitAddSub(&var1[shift], var1units-shift, + var2, var2units, 0, + &var1[shift], -mult); + #if DECTRACE + decDumpAr('#', &var1[shift], var1units-shift); + #endif + // var1 now probably has leading zeros; these are removed at the + // top of the inner loop. + } // inner loop + + // The next unit has been calculated in full; unless it's a + // leading zero, add to acc + if (accunits!=0 || thisunit!=0) { // is first or non-zero + *accnext=thisunit; // store in accumulator + // account exactly for the new digits + if (accunits==0) { + accdigits++; // at least one + for (pow=&powers[1]; thisunit>=*pow; pow++) accdigits++; + } + else accdigits+=DECDPUN; + accunits++; // update count + accnext--; // ready for next + if (accdigits>reqdigits) break; // have enough digits + } + + // if the residue is zero, the operation is done (unless divide + // or divideInteger and still not enough digits yet) + if (*var1==0 && var1units==1) { // residue is 0 + if (op&(REMAINDER|REMNEAR)) break; + if ((op&DIVIDE) && (exponent<=maxexponent)) break; + // [drop through if divideInteger] + } + // also done enough if calculating remainder or integer + // divide and just did the last ('units') unit + if (exponent==0 && !(op&DIVIDE)) break; + + // to get here, var1 is less than var2, so divide var2 by the per- + // Unit power of ten and go for the next digit + var2ulen--; // shift down + exponent-=DECDPUN; // update the exponent + } // outer loop + + // ---- division is complete --------------------------------------- + // here: acc has at least reqdigits+1 of good results (or fewer + // if early stop), starting at accnext+1 (its lsu) + // var1 has any residue at the stopping point + // accunits is the number of digits collected in acc + if (accunits==0) { // acc is 0 + accunits=1; // show have a unit .. + accdigits=1; // .. + *accnext=0; // .. whose value is 0 + } + else accnext++; // back to last placed + // accnext now -> lowest unit of result + + residue=0; // assume no residue + if (op&DIVIDE) { + // record the presence of any residue, for rounding + if (*var1!=0 || var1units>1) residue=1; + else { // no residue + // Had an exact division; clean up spurious trailing 0s. + // There will be at most DECDPUN-1, from the final multiply, + // and then only if the result is non-0 (and even) and the + // exponent is 'loose'. + #if DECDPUN>1 + Unit lsu=*accnext; + if (!(lsu&0x01) && (lsu!=0)) { + // count the trailing zeros + Int drop=0; + for (;; drop++) { // [will terminate because lsu!=0] + if (exponent>=maxexponent) break; // don't chop real 0s + #if DECDPUN<=4 + if ((lsu-QUOT10(lsu, drop+1) + *powers[drop+1])!=0) break; // found non-0 digit + #else + if (lsu%powers[drop+1]!=0) break; // found non-0 digit + #endif + exponent++; + } + if (drop>0) { + accunits=decShiftToLeast(accnext, accunits, drop); + accdigits=decGetDigits(accnext, accunits); + accunits=D2U(accdigits); + // [exponent was adjusted in the loop] + } + } // neither odd nor 0 + #endif + } // exact divide + } // divide + else /* op!=DIVIDE */ { + // check for coefficient overflow + if (accdigits+exponent>reqdigits) { + *status|=DEC_Division_impossible; + break; + } + if (op & (REMAINDER|REMNEAR)) { + // [Here, the exponent will be 0, because var1 was adjusted + // appropriately.] + Int postshift; // work + Flag wasodd=0; // integer was odd + Unit *quotlsu; // for save + Int quotdigits; // .. + + bits=lhs->bits; // remainder sign is always as lhs + + // Fastpath when residue is truly 0 is worthwhile [and + // simplifies the code below] + if (*var1==0 && var1units==1) { // residue is 0 + Int exp=lhs->exponent; // save min(exponents) + if (rhs->exponentexponent; + decNumberZero(res); // 0 coefficient + #if DECSUBSET + if (set->extended) + #endif + res->exponent=exp; // .. with proper exponent + res->bits=(uByte)(bits&DECNEG); // [cleaned] + decFinish(res, set, &residue, status); // might clamp + break; + } + // note if the quotient was odd + if (*accnext & 0x01) wasodd=1; // acc is odd + quotlsu=accnext; // save in case need to reinspect + quotdigits=accdigits; // .. + + // treat the residue, in var1, as the value to return, via acc + // calculate the unused zero digits. This is the smaller of: + // var1 initial padding (saved above) + // var2 residual padding, which happens to be given by: + postshift=var1initpad+exponent-lhs->exponent+rhs->exponent; + // [the 'exponent' term accounts for the shifts during divide] + if (var1initpadexponent; // exponent is smaller of lhs & rhs + if (rhs->exponentexponent; + + // Now correct the result if doing remainderNear; if it + // (looking just at coefficients) is > rhs/2, or == rhs/2 and + // the integer was odd then the result should be rem-rhs. + if (op&REMNEAR) { + Int compare, tarunits; // work + Unit *up; // .. + // calculate remainder*2 into the var1 buffer (which has + // 'headroom' of an extra unit and hence enough space) + // [a dedicated 'double' loop would be faster, here] + tarunits=decUnitAddSub(accnext, accunits, accnext, accunits, + 0, accnext, 1); + // decDumpAr('r', accnext, tarunits); + + // Here, accnext (var1) holds tarunits Units with twice the + // remainder's coefficient, which must now be compared to the + // RHS. The remainder's exponent may be smaller than the RHS's. + compare=decUnitCompare(accnext, tarunits, rhs->lsu, D2U(rhs->digits), + rhs->exponent-exponent); + if (compare==BADINT) { // deep trouble + *status|=DEC_Insufficient_storage; + break;} + + // now restore the remainder by dividing by two; the lsu + // is known to be even. + for (up=accnext; up0 || (compare==0 && wasodd)) { // adjustment needed + Int exp, expunits, exprem; // work + // This is effectively causing round-up of the quotient, + // so if it was the rare case where it was full and all + // nines, it would overflow and hence division-impossible + // should be raised + Flag allnines=0; // 1 if quotient all nines + if (quotdigits==reqdigits) { // could be borderline + for (up=quotlsu; ; up++) { + if (quotdigits>DECDPUN) { + if (*up!=DECDPUNMAX) break;// non-nines + } + else { // this is the last Unit + if (*up==powers[quotdigits]-1) allnines=1; + break; + } + quotdigits-=DECDPUN; // checked those digits + } // up + } // borderline check + if (allnines) { + *status|=DEC_Division_impossible; + break;} + + // rem-rhs is needed; the sign will invert. Again, var1 + // can safely be used for the working Units array. + exp=rhs->exponent-exponent; // RHS padding needed + // Calculate units and remainder from exponent. + expunits=exp/DECDPUN; + exprem=exp%DECDPUN; + // subtract [A+B*(-m)]; the result will always be negative + accunits=-decUnitAddSub(accnext, accunits, + rhs->lsu, D2U(rhs->digits), + expunits, accnext, -(Int)powers[exprem]); + accdigits=decGetDigits(accnext, accunits); // count digits exactly + accunits=D2U(accdigits); // and recalculate the units for copy + // [exponent is as for original remainder] + bits^=DECNEG; // flip the sign + } + } // REMNEAR + } // REMAINDER or REMNEAR + } // not DIVIDE + + // Set exponent and bits + res->exponent=exponent; + res->bits=(uByte)(bits&DECNEG); // [cleaned] + + // Now the coefficient. + decSetCoeff(res, set, accnext, accdigits, &residue, status); + + decFinish(res, set, &residue, status); // final cleanup + + #if DECSUBSET + // If a divide then strip trailing zeros if subset [after round] + if (!set->extended && (op==DIVIDE)) decTrim(res, set, 0, 1, &dropped); + #endif + } while(0); // end protected + + if (varalloc!=NULL) free(varalloc); // drop any storage used + if (allocacc!=NULL) free(allocacc); // .. + #if DECSUBSET + if (allocrhs!=NULL) free(allocrhs); // .. + if (alloclhs!=NULL) free(alloclhs); // .. + #endif + return res; + } // decDivideOp + +/* ------------------------------------------------------------------ */ +/* decMultiplyOp -- multiplication operation */ +/* */ +/* This routine performs the multiplication C=A x B. */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X*X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* status is the usual accumulator */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* ------------------------------------------------------------------ */ +/* 'Classic' multiplication is used rather than Karatsuba, as the */ +/* latter would give only a minor improvement for the short numbers */ +/* expected to be handled most (and uses much more memory). */ +/* */ +/* There are two major paths here: the general-purpose ('old code') */ +/* path which handles all DECDPUN values, and a fastpath version */ +/* which is used if 64-bit ints are available, DECDPUN<=4, and more */ +/* than two calls to decUnitAddSub would be made. */ +/* */ +/* The fastpath version lumps units together into 8-digit or 9-digit */ +/* chunks, and also uses a lazy carry strategy to minimise expensive */ +/* 64-bit divisions. The chunks are then broken apart again into */ +/* units for continuing processing. Despite this overhead, the */ +/* fastpath can speed up some 16-digit operations by 10x (and much */ +/* more for higher-precision calculations). */ +/* */ +/* A buffer always has to be used for the accumulator; in the */ +/* fastpath, buffers are also always needed for the chunked copies of */ +/* of the operand coefficients. */ +/* Static buffers are larger than needed just for multiply, to allow */ +/* for calls from other operations (notably exp). */ +/* ------------------------------------------------------------------ */ +#define FASTMUL (DECUSE64 && DECDPUN<5) +static decNumber * decMultiplyOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + uInt *status) { + Int accunits; // Units of accumulator in use + Int exponent; // work + Int residue=0; // rounding residue + uByte bits; // result sign + Unit *acc; // -> accumulator Unit array + Int needbytes; // size calculator + void *allocacc=NULL; // -> allocated accumulator, iff allocated + Unit accbuff[SD2U(DECBUFFER*4+1)]; // buffer (+1 for DECBUFFER==0, + // *4 for calls from other operations) + const Unit *mer, *mermsup; // work + Int madlength; // Units in multiplicand + Int shift; // Units to shift multiplicand by + + #if FASTMUL + // if DECDPUN is 1 or 3 work in base 10**9, otherwise + // (DECDPUN is 2 or 4) then work in base 10**8 + #if DECDPUN & 1 // odd + #define FASTBASE 1000000000 // base + #define FASTDIGS 9 // digits in base + #define FASTLAZY 18 // carry resolution point [1->18] + #else + #define FASTBASE 100000000 + #define FASTDIGS 8 + #define FASTLAZY 1844 // carry resolution point [1->1844] + #endif + // three buffers are used, two for chunked copies of the operands + // (base 10**8 or base 10**9) and one base 2**64 accumulator with + // lazy carry evaluation + uInt zlhibuff[(DECBUFFER*2+1)/8+1]; // buffer (+1 for DECBUFFER==0) + uInt *zlhi=zlhibuff; // -> lhs array + uInt *alloclhi=NULL; // -> allocated buffer, iff allocated + uInt zrhibuff[(DECBUFFER*2+1)/8+1]; // buffer (+1 for DECBUFFER==0) + uInt *zrhi=zrhibuff; // -> rhs array + uInt *allocrhi=NULL; // -> allocated buffer, iff allocated + uLong zaccbuff[(DECBUFFER*2+1)/4+2]; // buffer (+1 for DECBUFFER==0) + // [allocacc is shared for both paths, as only one will run] + uLong *zacc=zaccbuff; // -> accumulator array for exact result + #if DECDPUN==1 + Int zoff; // accumulator offset + #endif + uInt *lip, *rip; // item pointers + uInt *lmsi, *rmsi; // most significant items + Int ilhs, irhs, iacc; // item counts in the arrays + Int lazy; // lazy carry counter + uLong lcarry; // uLong carry + uInt carry; // carry (NB not uLong) + Int count; // work + const Unit *cup; // .. + Unit *up; // .. + uLong *lp; // .. + Int p; // .. + #endif + + #if DECSUBSET + decNumber *alloclhs=NULL; // -> allocated buffer, iff allocated + decNumber *allocrhs=NULL; // -> allocated buffer, iff allocated + #endif + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + // precalculate result sign + bits=(uByte)((lhs->bits^rhs->bits)&DECNEG); + + // handle infinities and NaNs + if (SPECIALARGS) { // a special bit set + if (SPECIALARGS & (DECSNAN | DECNAN)) { // one or two NaNs + decNaNs(res, lhs, rhs, set, status); + return res;} + // one or two infinities; Infinity * 0 is invalid + if (((lhs->bits & DECINF)==0 && ISZERO(lhs)) + ||((rhs->bits & DECINF)==0 && ISZERO(rhs))) { + *status|=DEC_Invalid_operation; + return res;} + decNumberZero(res); + res->bits=bits|DECINF; // infinity + return res;} + + // For best speed, as in DMSRCN [the original Rexx numerics + // module], use the shorter number as the multiplier (rhs) and + // the longer as the multiplicand (lhs) to minimise the number of + // adds (partial products) + if (lhs->digitsdigits) { // swap... + const decNumber *hold=lhs; + lhs=rhs; + rhs=hold; + } + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operands and set lostDigits status, as needed + if (lhs->digits>set->digits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==NULL) break; + lhs=alloclhs; + } + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + #if FASTMUL // fastpath can be used + // use the fast path if there are enough digits in the shorter + // operand to make the setup and takedown worthwhile + #define NEEDTWO (DECDPUN*2) // within two decUnitAddSub calls + if (rhs->digits>NEEDTWO) { // use fastpath... + // calculate the number of elements in each array + ilhs=(lhs->digits+FASTDIGS-1)/FASTDIGS; // [ceiling] + irhs=(rhs->digits+FASTDIGS-1)/FASTDIGS; // .. + iacc=ilhs+irhs; + + // allocate buffers if required, as usual + needbytes=ilhs*sizeof(uInt); + if (needbytes>(Int)sizeof(zlhibuff)) { + alloclhi=(uInt *)malloc(needbytes); + zlhi=alloclhi;} + needbytes=irhs*sizeof(uInt); + if (needbytes>(Int)sizeof(zrhibuff)) { + allocrhi=(uInt *)malloc(needbytes); + zrhi=allocrhi;} + + // Allocating the accumulator space needs a special case when + // DECDPUN=1 because when converting the accumulator to Units + // after the multiplication each 8-byte item becomes 9 1-byte + // units. Therefore iacc extra bytes are needed at the front + // (rounded up to a multiple of 8 bytes), and the uLong + // accumulator starts offset the appropriate number of units + // to the right to avoid overwrite during the unchunking. + needbytes=iacc*sizeof(uLong); + #if DECDPUN==1 + zoff=(iacc+7)/8; // items to offset by + needbytes+=zoff*8; + #endif + if (needbytes>(Int)sizeof(zaccbuff)) { + allocacc=(uLong *)malloc(needbytes); + zacc=(uLong *)allocacc;} + if (zlhi==NULL||zrhi==NULL||zacc==NULL) { + *status|=DEC_Insufficient_storage; + break;} + + acc=(Unit *)zacc; // -> target Unit array + #if DECDPUN==1 + zacc+=zoff; // start uLong accumulator to right + #endif + + // assemble the chunked copies of the left and right sides + for (count=lhs->digits, cup=lhs->lsu, lip=zlhi; count>0; lip++) + for (p=0, *lip=0; p0; + p+=DECDPUN, cup++, count-=DECDPUN) + *lip+=*cup*powers[p]; + lmsi=lip-1; // save -> msi + for (count=rhs->digits, cup=rhs->lsu, rip=zrhi; count>0; rip++) + for (p=0, *rip=0; p0; + p+=DECDPUN, cup++, count-=DECDPUN) + *rip+=*cup*powers[p]; + rmsi=rip-1; // save -> msi + + // zero the accumulator + for (lp=zacc; lp0 && rip!=rmsi) continue; + lazy=FASTLAZY; // reset delay count + // spin up the accumulator resolving overflows + for (lp=zacc; lp assume buffer for accumulator + needbytes=(D2U(lhs->digits)+D2U(rhs->digits))*sizeof(Unit); + if (needbytes>(Int)sizeof(accbuff)) { + allocacc=(Unit *)malloc(needbytes); + if (allocacc==NULL) {*status|=DEC_Insufficient_storage; break;} + acc=(Unit *)allocacc; // use the allocated space + } + + /* Now the main long multiplication loop */ + // Unlike the equivalent in the IBM Java implementation, there + // is no advantage in calculating from msu to lsu. So, do it + // by the book, as it were. + // Each iteration calculates ACC=ACC+MULTAND*MULT + accunits=1; // accumulator starts at '0' + *acc=0; // .. (lsu=0) + shift=0; // no multiplicand shift at first + madlength=D2U(lhs->digits); // this won't change + mermsup=rhs->lsu+D2U(rhs->digits); // -> msu+1 of multiplier + + for (mer=rhs->lsu; merlsu, madlength, 0, + &acc[shift], *mer) + + shift; + else { // extend acc with a 0; it will be used shortly + *(acc+accunits)=0; // [this avoids length of <=0 later] + accunits++; + } + // multiply multiplicand by 10**DECDPUN for next Unit to left + shift++; // add this for 'logical length' + } // n + #if FASTMUL + } // unchunked units + #endif + // common end-path + #if DECTRACE + decDumpAr('*', acc, accunits); // Show exact result + #endif + + // acc now contains the exact result of the multiplication, + // possibly with a leading zero unit; build the decNumber from + // it, noting if any residue + res->bits=bits; // set sign + res->digits=decGetDigits(acc, accunits); // count digits exactly + + // There can be a 31-bit wrap in calculating the exponent. + // This can only happen if both input exponents are negative and + // both their magnitudes are large. If there was a wrap, set a + // safe very negative exponent, from which decFinalize() will + // raise a hard underflow shortly. + exponent=lhs->exponent+rhs->exponent; // calculate exponent + if (lhs->exponent<0 && rhs->exponent<0 && exponent>0) + exponent=-2*DECNUMMAXE; // force underflow + res->exponent=exponent; // OK to overwrite now + + + // Set the coefficient. If any rounding, residue records + decSetCoeff(res, set, acc, res->digits, &residue, status); + decFinish(res, set, &residue, status); // final cleanup + } while(0); // end protected + + if (allocacc!=NULL) free(allocacc); // drop any storage used + #if DECSUBSET + if (allocrhs!=NULL) free(allocrhs); // .. + if (alloclhs!=NULL) free(alloclhs); // .. + #endif + #if FASTMUL + if (allocrhi!=NULL) free(allocrhi); // .. + if (alloclhi!=NULL) free(alloclhi); // .. + #endif + return res; + } // decMultiplyOp + +/* ------------------------------------------------------------------ */ +/* decExpOp -- effect exponentiation */ +/* */ +/* This computes C = exp(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. status is updated but */ +/* not set. */ +/* */ +/* Restrictions: */ +/* */ +/* digits, emax, and -emin in the context must be less than */ +/* 2*DEC_MAX_MATH (1999998), and the rhs must be within these */ +/* bounds or a zero. This is an internal routine, so these */ +/* restrictions are contractual and not enforced. */ +/* */ +/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* */ +/* Finite results will always be full precision and Inexact, except */ +/* when A is a zero or -Infinity (giving 1 or 0 respectively). */ +/* ------------------------------------------------------------------ */ +/* This approach used here is similar to the algorithm described in */ +/* */ +/* Variable Precision Exponential Function, T. E. Hull and */ +/* A. Abrham, ACM Transactions on Mathematical Software, Vol 12 #2, */ +/* pp79-91, ACM, June 1986. */ +/* */ +/* with the main difference being that the iterations in the series */ +/* evaluation are terminated dynamically (which does not require the */ +/* extra variable-precision variables which are expensive in this */ +/* context). */ +/* */ +/* The error analysis in Hull & Abrham's paper applies except for the */ +/* round-off error accumulation during the series evaluation. This */ +/* code does not precalculate the number of iterations and so cannot */ +/* use Horner's scheme. Instead, the accumulation is done at double- */ +/* precision, which ensures that the additions of the terms are exact */ +/* and do not accumulate round-off (and any round-off errors in the */ +/* terms themselves move 'to the right' faster than they can */ +/* accumulate). This code also extends the calculation by allowing, */ +/* in the spirit of other decNumber operators, the input to be more */ +/* precise than the result (the precision used is based on the more */ +/* precise of the input or requested result). */ +/* */ +/* Implementation notes: */ +/* */ +/* 1. This is separated out as decExpOp so it can be called from */ +/* other Mathematical functions (notably Ln) with a wider range */ +/* than normal. In particular, it can handle the slightly wider */ +/* (double) range needed by Ln (which has to be able to calculate */ +/* exp(-x) where x can be the tiniest number (Ntiny). */ +/* */ +/* 2. Normalizing x to be <=0.1 (instead of <=1) reduces loop */ +/* iterations by appoximately a third with additional (although */ +/* diminishing) returns as the range is reduced to even smaller */ +/* fractions. However, h (the power of 10 used to correct the */ +/* result at the end, see below) must be kept <=8 as otherwise */ +/* the final result cannot be computed. Hence the leverage is a */ +/* sliding value (8-h), where potentially the range is reduced */ +/* more for smaller values. */ +/* */ +/* The leverage that can be applied in this way is severely */ +/* limited by the cost of the raise-to-the power at the end, */ +/* which dominates when the number of iterations is small (less */ +/* than ten) or when rhs is short. As an example, the adjustment */ +/* x**10,000,000 needs 31 multiplications, all but one full-width. */ +/* */ +/* 3. The restrictions (especially precision) could be raised with */ +/* care, but the full decNumber range seems very hard within the */ +/* 32-bit limits. */ +/* */ +/* 4. The working precisions for the static buffers are twice the */ +/* obvious size to allow for calls from decNumberPower. */ +/* ------------------------------------------------------------------ */ +decNumber * decExpOp(decNumber *res, const decNumber *rhs, + decContext *set, uInt *status) { + uInt ignore=0; // working status + Int h; // adjusted exponent for 0.xxxx + Int p; // working precision + Int residue; // rounding residue + uInt needbytes; // for space calculations + const decNumber *x=rhs; // (may point to safe copy later) + decContext aset, tset, dset; // working contexts + Int comp; // work + + // the argument is often copied to normalize it, so (unusually) it + // is treated like other buffers, using DECBUFFER, +1 in case + // DECBUFFER is 0 + decNumber bufr[D2N(DECBUFFER*2+1)]; + decNumber *allocrhs=NULL; // non-NULL if rhs buffer allocated + + // the working precision will be no more than set->digits+8+1 + // so for on-stack buffers DECBUFFER+9 is used, +1 in case DECBUFFER + // is 0 (and twice that for the accumulator) + + // buffer for t, term (working precision plus) + decNumber buft[D2N(DECBUFFER*2+9+1)]; + decNumber *allocbuft=NULL; // -> allocated buft, iff allocated + decNumber *t=buft; // term + // buffer for a, accumulator (working precision * 2), at least 9 + decNumber bufa[D2N(DECBUFFER*4+18+1)]; + decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated + decNumber *a=bufa; // accumulator + // decNumber for the divisor term; this needs at most 9 digits + // and so can be fixed size [16 so can use standard context] + decNumber bufd[D2N(16)]; + decNumber *d=bufd; // divisor + decNumber numone; // constant 1 + + #if DECCHECK + Int iterations=0; // for later sanity check + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { // protect allocated storage + if (SPECIALARG) { // handle infinities and NaNs + if (decNumberIsInfinite(rhs)) { // an infinity + if (decNumberIsNegative(rhs)) // -Infinity -> +0 + decNumberZero(res); + else decNumberCopy(res, rhs); // +Infinity -> self + } + else decNaNs(res, rhs, NULL, set, status); // a NaN + break;} + + if (ISZERO(rhs)) { // zeros -> exact 1 + decNumberZero(res); // make clean 1 + *res->lsu=1; // .. + break;} // [no status to set] + + // e**x when 0 < x < 0.66 is < 1+3x/2, hence can fast-path + // positive and negative tiny cases which will result in inexact + // 1. This also allows the later add-accumulate to always be + // exact (because its length will never be more than twice the + // working precision). + // The comparator (tiny) needs just one digit, so use the + // decNumber d for it (reused as the divisor, etc., below); its + // exponent is such that if x is positive it will have + // set->digits-1 zeros between the decimal point and the digit, + // which is 4, and if x is negative one more zero there as the + // more precise result will be of the form 0.9999999 rather than + // 1.0000001. Hence, tiny will be 0.0000004 if digits=7 and x>0 + // or 0.00000004 if digits=7 and x<0. If RHS not larger than + // this then the result will be 1.000000 + decNumberZero(d); // clean + *d->lsu=4; // set 4 .. + d->exponent=-set->digits; // * 10**(-d) + if (decNumberIsNegative(rhs)) d->exponent--; // negative case + comp=decCompare(d, rhs, 1); // signless compare + if (comp==BADINT) { + *status|=DEC_Insufficient_storage; + break;} + if (comp>=0) { // rhs < d + Int shift=set->digits-1; + decNumberZero(res); // set 1 + *res->lsu=1; // .. + res->digits=decShiftToMost(res->lsu, 1, shift); + res->exponent=-shift; // make 1.0000... + *status|=DEC_Inexact | DEC_Rounded; // .. inexactly + break;} // tiny + + // set up the context to be used for calculating a, as this is + // used on both paths below + decContextDefault(&aset, DEC_INIT_DECIMAL64); + // accumulator bounds are as requested (could underflow) + aset.emax=set->emax; // usual bounds + aset.emin=set->emin; // .. + aset.clamp=0; // and no concrete format + + // calculate the adjusted (Hull & Abrham) exponent (where the + // decimal point is just to the left of the coefficient msd) + h=rhs->exponent+rhs->digits; + // if h>8 then 10**h cannot be calculated safely; however, when + // h=8 then exp(|rhs|) will be at least exp(1E+7) which is at + // least 6.59E+4342944, so (due to the restriction on Emax/Emin) + // overflow (or underflow to 0) is guaranteed -- so this case can + // be handled by simply forcing the appropriate excess + if (h>8) { // overflow/underflow + // set up here so Power call below will over or underflow to + // zero; set accumulator to either 2 or 0.02 + // [stack buffer for a is always big enough for this] + decNumberZero(a); + *a->lsu=2; // not 1 but < exp(1) + if (decNumberIsNegative(rhs)) a->exponent=-2; // make 0.02 + h=8; // clamp so 10**h computable + p=9; // set a working precision + } + else { // h<=8 + Int maxlever=(rhs->digits>8?1:0); + // [could/should increase this for precisions >40 or so, too] + + // if h is 8, cannot normalize to a lower upper limit because + // the final result will not be computable (see notes above), + // but leverage can be applied whenever h is less than 8. + // Apply as much as possible, up to a MAXLEVER digits, which + // sets the tradeoff against the cost of the later a**(10**h). + // As h is increased, the working precision below also + // increases to compensate for the "constant digits at the + // front" effect. + Int lever=MINI(8-h, maxlever); // leverage attainable + Int use=-rhs->digits-lever; // exponent to use for RHS + h+=lever; // apply leverage selected + if (h<0) { // clamp + use+=h; // [may end up subnormal] + h=0; + } + // Take a copy of RHS if it needs normalization (true whenever x>=1) + if (rhs->exponent!=use) { + decNumber *newrhs=bufr; // assume will fit on stack + needbytes=sizeof(decNumber)+(D2U(rhs->digits)-1)*sizeof(Unit); + if (needbytes>sizeof(bufr)) { // need malloc space + allocrhs=(decNumber *)malloc(needbytes); + if (allocrhs==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + newrhs=allocrhs; // use the allocated space + } + decNumberCopy(newrhs, rhs); // copy to safe space + newrhs->exponent=use; // normalize; now <1 + x=newrhs; // ready for use + // decNumberShow(x); + } + + // Now use the usual power series to evaluate exp(x). The + // series starts as 1 + x + x^2/2 ... so prime ready for the + // third term by setting the term variable t=x, the accumulator + // a=1, and the divisor d=2. + + // First determine the working precision. From Hull & Abrham + // this is set->digits+h+2. However, if x is 'over-precise' we + // need to allow for all its digits to potentially participate + // (consider an x where all the excess digits are 9s) so in + // this case use x->digits+h+2 + p=MAXI(x->digits, set->digits)+h+2; // [h<=8] + + // a and t are variable precision, and depend on p, so space + // must be allocated for them if necessary + + // the accumulator needs to be able to hold 2p digits so that + // the additions on the second and subsequent iterations are + // sufficiently exact. + needbytes=sizeof(decNumber)+(D2U(p*2)-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { // need malloc space + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + a=allocbufa; // use the allocated space + } + // the term needs to be able to hold p digits (which is + // guaranteed to be larger than x->digits, so the initial copy + // is safe); it may also be used for the raise-to-power + // calculation below, which needs an extra two digits + needbytes=sizeof(decNumber)+(D2U(p+2)-1)*sizeof(Unit); + if (needbytes>sizeof(buft)) { // need malloc space + allocbuft=(decNumber *)malloc(needbytes); + if (allocbuft==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + t=allocbuft; // use the allocated space + } + + decNumberCopy(t, x); // term=x + decNumberZero(a); *a->lsu=1; // accumulator=1 + decNumberZero(d); *d->lsu=2; // divisor=2 + decNumberZero(&numone); *numone.lsu=1; // constant 1 for increment + + // set up the contexts for calculating a, t, and d + decContextDefault(&tset, DEC_INIT_DECIMAL64); + dset=tset; + // accumulator bounds are set above, set precision now + aset.digits=p*2; // double + // term bounds avoid any underflow or overflow + tset.digits=p; + tset.emin=DEC_MIN_EMIN; // [emax is plenty] + // [dset.digits=16, etc., are sufficient] + + // finally ready to roll + for (;;) { + #if DECCHECK + iterations++; + #endif + // only the status from the accumulation is interesting + // [but it should remain unchanged after first add] + decAddOp(a, a, t, &aset, 0, status); // a=a+t + decMultiplyOp(t, t, x, &tset, &ignore); // t=t*x + decDivideOp(t, t, d, &tset, DIVIDE, &ignore); // t=t/d + // the iteration ends when the term cannot affect the result, + // if rounded to p digits, which is when its value is smaller + // than the accumulator by p+1 digits. There must also be + // full precision in a. + if (((a->digits+a->exponent)>=(t->digits+t->exponent+p+1)) + && (a->digits>=p)) break; + decAddOp(d, d, &numone, &dset, 0, &ignore); // d=d+1 + } // iterate + + #if DECCHECK + // just a sanity check; comment out test to show always + if (iterations>p+3) + printf("Exp iterations=%ld, status=%08lx, p=%ld, d=%ld\n", + (LI)iterations, (LI)*status, (LI)p, (LI)x->digits); + #endif + } // h<=8 + + // apply postconditioning: a=a**(10**h) -- this is calculated + // at a slightly higher precision than Hull & Abrham suggest + if (h>0) { + Int seenbit=0; // set once a 1-bit is seen + Int i; // counter + Int n=powers[h]; // always positive + aset.digits=p+2; // sufficient precision + // avoid the overhead and many extra digits of decNumberPower + // as all that is needed is the short 'multipliers' loop; here + // accumulate the answer into t + decNumberZero(t); *t->lsu=1; // acc=1 + for (i=1;;i++){ // for each bit [top bit ignored] + // abandon if have had overflow or terminal underflow + if (*status & (DEC_Overflow|DEC_Underflow)) { // interesting? + if (*status&DEC_Overflow || ISZERO(t)) break;} + n=n<<1; // move next bit to testable position + if (n<0) { // top bit is set + seenbit=1; // OK, have a significant bit + decMultiplyOp(t, t, a, &aset, status); // acc=acc*x + } + if (i==31) break; // that was the last bit + if (!seenbit) continue; // no need to square 1 + decMultiplyOp(t, t, t, &aset, status); // acc=acc*acc [square] + } /*i*/ // 32 bits + // decNumberShow(t); + a=t; // and carry on using t instead of a + } + + // Copy and round the result to res + residue=1; // indicate dirt to right .. + if (ISZERO(a)) residue=0; // .. unless underflowed to 0 + aset.digits=set->digits; // [use default rounding] + decCopyFit(res, a, &aset, &residue, status); // copy & shorten + decFinish(res, set, &residue, status); // cleanup/set flags + } while(0); // end protected + + if (allocrhs !=NULL) free(allocrhs); // drop any storage used + if (allocbufa!=NULL) free(allocbufa); // .. + if (allocbuft!=NULL) free(allocbuft); // .. + // [status is handled by caller] + return res; + } // decExpOp + +/* ------------------------------------------------------------------ */ +/* Initial-estimate natural logarithm table */ +/* */ +/* LNnn -- 90-entry 16-bit table for values from .10 through .99. */ +/* The result is a 4-digit encode of the coefficient (c=the */ +/* top 14 bits encoding 0-9999) and a 2-digit encode of the */ +/* exponent (e=the bottom 2 bits encoding 0-3) */ +/* */ +/* The resulting value is given by: */ +/* */ +/* v = -c * 10**(-e-3) */ +/* */ +/* where e and c are extracted from entry k = LNnn[x-10] */ +/* where x is truncated (NB) into the range 10 through 99, */ +/* and then c = k>>2 and e = k&3. */ +/* ------------------------------------------------------------------ */ +const uShort LNnn[90]={9016, 8652, 8316, 8008, 7724, 7456, 7208, + 6972, 6748, 6540, 6340, 6148, 5968, 5792, 5628, 5464, 5312, + 5164, 5020, 4884, 4748, 4620, 4496, 4376, 4256, 4144, 4032, + 39233, 38181, 37157, 36157, 35181, 34229, 33297, 32389, 31501, 30629, + 29777, 28945, 28129, 27329, 26545, 25777, 25021, 24281, 23553, 22837, + 22137, 21445, 20769, 20101, 19445, 18801, 18165, 17541, 16925, 16321, + 15721, 15133, 14553, 13985, 13421, 12865, 12317, 11777, 11241, 10717, + 10197, 9685, 9177, 8677, 8185, 7697, 7213, 6737, 6269, 5801, + 5341, 4889, 4437, 39930, 35534, 31186, 26886, 22630, 18418, 14254, + 10130, 6046, 20055}; + +/* ------------------------------------------------------------------ */ +/* decLnOp -- effect natural logarithm */ +/* */ +/* This computes C = ln(A) */ +/* */ +/* res is C, the result. C may be A */ +/* rhs is A */ +/* set is the context; note that rounding mode has no effect */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Notable cases: */ +/* A<0 -> Invalid */ +/* A=0 -> -Infinity (Exact) */ +/* A=+Infinity -> +Infinity (Exact) */ +/* A=1 exactly -> 0 (Exact) */ +/* */ +/* Restrictions (as for Exp): */ +/* */ +/* digits, emax, and -emin in the context must be less than */ +/* DEC_MAX_MATH+11 (1000010), and the rhs must be within these */ +/* bounds or a zero. This is an internal routine, so these */ +/* restrictions are contractual and not enforced. */ +/* */ +/* A finite result is rounded using DEC_ROUND_HALF_EVEN; it will */ +/* almost always be correctly rounded, but may be up to 1 ulp in */ +/* error in rare cases. */ +/* ------------------------------------------------------------------ */ +/* The result is calculated using Newton's method, with each */ +/* iteration calculating a' = a + x * exp(-a) - 1. See, for example, */ +/* Epperson 1989. */ +/* */ +/* The iteration ends when the adjustment x*exp(-a)-1 is tiny enough. */ +/* This has to be calculated at the sum of the precision of x and the */ +/* working precision. */ +/* */ +/* Implementation notes: */ +/* */ +/* 1. This is separated out as decLnOp so it can be called from */ +/* other Mathematical functions (e.g., Log 10) with a wider range */ +/* than normal. In particular, it can handle the slightly wider */ +/* (+9+2) range needed by a power function. */ +/* */ +/* 2. The speed of this function is about 10x slower than exp, as */ +/* it typically needs 4-6 iterations for short numbers, and the */ +/* extra precision needed adds a squaring effect, twice. */ +/* */ +/* 3. Fastpaths are included for ln(10) and ln(2), up to length 40, */ +/* as these are common requests. ln(10) is used by log10(x). */ +/* */ +/* 4. An iteration might be saved by widening the LNnn table, and */ +/* would certainly save at least one if it were made ten times */ +/* bigger, too (for truncated fractions 0.100 through 0.999). */ +/* However, for most practical evaluations, at least four or five */ +/* iterations will be neede -- so this would only speed up by */ +/* 20-25% and that probably does not justify increasing the table */ +/* size. */ +/* */ +/* 5. The static buffers are larger than might be expected to allow */ +/* for calls from decNumberPower. */ +/* ------------------------------------------------------------------ */ +decNumber * decLnOp(decNumber *res, const decNumber *rhs, + decContext *set, uInt *status) { + uInt ignore=0; // working status accumulator + uInt needbytes; // for space calculations + Int residue; // rounding residue + Int r; // rhs=f*10**r [see below] + Int p; // working precision + Int pp; // precision for iteration + Int t; // work + + // buffers for a (accumulator, typically precision+2) and b + // (adjustment calculator, same size) + decNumber bufa[D2N(DECBUFFER+12)]; + decNumber *allocbufa=NULL; // -> allocated bufa, iff allocated + decNumber *a=bufa; // accumulator/work + decNumber bufb[D2N(DECBUFFER*2+2)]; + decNumber *allocbufb=NULL; // -> allocated bufa, iff allocated + decNumber *b=bufb; // adjustment/work + + decNumber numone; // constant 1 + decNumber cmp; // work + decContext aset, bset; // working contexts + + #if DECCHECK + Int iterations=0; // for later sanity check + if (decCheckOperands(res, DECUNUSED, rhs, set)) return res; + #endif + + do { // protect allocated storage + if (SPECIALARG) { // handle infinities and NaNs + if (decNumberIsInfinite(rhs)) { // an infinity + if (decNumberIsNegative(rhs)) // -Infinity -> error + *status|=DEC_Invalid_operation; + else decNumberCopy(res, rhs); // +Infinity -> self + } + else decNaNs(res, rhs, NULL, set, status); // a NaN + break;} + + if (ISZERO(rhs)) { // +/- zeros -> -Infinity + decNumberZero(res); // make clean + res->bits=DECINF|DECNEG; // set - infinity + break;} // [no status to set] + + // Non-zero negatives are bad... + if (decNumberIsNegative(rhs)) { // -x -> error + *status|=DEC_Invalid_operation; + break;} + + // Here, rhs is positive, finite, and in range + + // lookaside fastpath code for ln(2) and ln(10) at common lengths + if (rhs->exponent==0 && set->digits<=40) { + #if DECDPUN==1 + if (rhs->lsu[0]==0 && rhs->lsu[1]==1 && rhs->digits==2) { // ln(10) + #else + if (rhs->lsu[0]==10 && rhs->digits==2) { // ln(10) + #endif + aset=*set; aset.round=DEC_ROUND_HALF_EVEN; + #define LN10 "2.302585092994045684017991454684364207601" + decNumberFromString(res, LN10, &aset); + *status|=(DEC_Inexact | DEC_Rounded); // is inexact + break;} + if (rhs->lsu[0]==2 && rhs->digits==1) { // ln(2) + aset=*set; aset.round=DEC_ROUND_HALF_EVEN; + #define LN2 "0.6931471805599453094172321214581765680755" + decNumberFromString(res, LN2, &aset); + *status|=(DEC_Inexact | DEC_Rounded); + break;} + } // integer and short + + // Determine the working precision. This is normally the + // requested precision + 2, with a minimum of 9. However, if + // the rhs is 'over-precise' then allow for all its digits to + // potentially participate (consider an rhs where all the excess + // digits are 9s) so in this case use rhs->digits+2. + p=MAXI(rhs->digits, MAXI(set->digits, 7))+2; + + // Allocate space for the accumulator and the high-precision + // adjustment calculator, if necessary. The accumulator must + // be able to hold p digits, and the adjustment up to + // rhs->digits+p digits. They are also made big enough for 16 + // digits so that they can be used for calculating the initial + // estimate. + needbytes=sizeof(decNumber)+(D2U(MAXI(p,16))-1)*sizeof(Unit); + if (needbytes>sizeof(bufa)) { // need malloc space + allocbufa=(decNumber *)malloc(needbytes); + if (allocbufa==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + a=allocbufa; // use the allocated space + } + pp=p+rhs->digits; + needbytes=sizeof(decNumber)+(D2U(MAXI(pp,16))-1)*sizeof(Unit); + if (needbytes>sizeof(bufb)) { // need malloc space + allocbufb=(decNumber *)malloc(needbytes); + if (allocbufb==NULL) { // hopeless -- abandon + *status|=DEC_Insufficient_storage; + break;} + b=allocbufb; // use the allocated space + } + + // Prepare an initial estimate in acc. Calculate this by + // considering the coefficient of x to be a normalized fraction, + // f, with the decimal point at far left and multiplied by + // 10**r. Then, rhs=f*10**r and 0.1<=f<1, and + // ln(x) = ln(f) + ln(10)*r + // Get the initial estimate for ln(f) from a small lookup + // table (see above) indexed by the first two digits of f, + // truncated. + + decContextDefault(&aset, DEC_INIT_DECIMAL64); // 16-digit extended + r=rhs->exponent+rhs->digits; // 'normalised' exponent + decNumberFromInt32(a, r); // a=r + decNumberFromInt32(b, 2302585); // b=ln(10) (2.302585) + b->exponent=-6; // .. + decMultiplyOp(a, a, b, &aset, &ignore); // a=a*b + // now get top two digits of rhs into b by simple truncate and + // force to integer + residue=0; // (no residue) + aset.digits=2; aset.round=DEC_ROUND_DOWN; + decCopyFit(b, rhs, &aset, &residue, &ignore); // copy & shorten + b->exponent=0; // make integer + t=decGetInt(b); // [cannot fail] + if (t<10) t=X10(t); // adjust single-digit b + t=LNnn[t-10]; // look up ln(b) + decNumberFromInt32(b, t>>2); // b=ln(b) coefficient + b->exponent=-(t&3)-3; // set exponent + b->bits=DECNEG; // ln(0.10)->ln(0.99) always -ve + aset.digits=16; aset.round=DEC_ROUND_HALF_EVEN; // restore + decAddOp(a, a, b, &aset, 0, &ignore); // acc=a+b + // the initial estimate is now in a, with up to 4 digits correct. + // When rhs is at or near Nmax the estimate will be low, so we + // will approach it from below, avoiding overflow when calling exp. + + decNumberZero(&numone); *numone.lsu=1; // constant 1 for adjustment + + // accumulator bounds are as requested (could underflow, but + // cannot overflow) + aset.emax=set->emax; + aset.emin=set->emin; + aset.clamp=0; // no concrete format + // set up a context to be used for the multiply and subtract + bset=aset; + bset.emax=DEC_MAX_MATH*2; // use double bounds for the + bset.emin=-DEC_MAX_MATH*2; // adjustment calculation + // [see decExpOp call below] + // for each iteration double the number of digits to calculate, + // up to a maximum of p + pp=9; // initial precision + // [initially 9 as then the sequence starts 7+2, 16+2, and + // 34+2, which is ideal for standard-sized numbers] + aset.digits=pp; // working context + bset.digits=pp+rhs->digits; // wider context + for (;;) { // iterate + #if DECCHECK + iterations++; + if (iterations>24) break; // consider 9 * 2**24 + #endif + // calculate the adjustment (exp(-a)*x-1) into b. This is a + // catastrophic subtraction but it really is the difference + // from 1 that is of interest. + // Use the internal entry point to Exp as it allows the double + // range for calculating exp(-a) when a is the tiniest subnormal. + a->bits^=DECNEG; // make -a + decExpOp(b, a, &bset, &ignore); // b=exp(-a) + a->bits^=DECNEG; // restore sign of a + // now multiply by rhs and subtract 1, at the wider precision + decMultiplyOp(b, b, rhs, &bset, &ignore); // b=b*rhs + decAddOp(b, b, &numone, &bset, DECNEG, &ignore); // b=b-1 + + // the iteration ends when the adjustment cannot affect the + // result by >=0.5 ulp (at the requested digits), which + // is when its value is smaller than the accumulator by + // set->digits+1 digits (or it is zero) -- this is a looser + // requirement than for Exp because all that happens to the + // accumulator after this is the final rounding (but note that + // there must also be full precision in a, or a=0). + + if (decNumberIsZero(b) || + (a->digits+a->exponent)>=(b->digits+b->exponent+set->digits+1)) { + if (a->digits==p) break; + if (decNumberIsZero(a)) { + decCompareOp(&cmp, rhs, &numone, &aset, COMPARE, &ignore); // rhs=1 ? + if (cmp.lsu[0]==0) a->exponent=0; // yes, exact 0 + else *status|=(DEC_Inexact | DEC_Rounded); // no, inexact + break; + } + // force padding if adjustment has gone to 0 before full length + if (decNumberIsZero(b)) b->exponent=a->exponent-p; + } + + // not done yet ... + decAddOp(a, a, b, &aset, 0, &ignore); // a=a+b for next estimate + if (pp==p) continue; // precision is at maximum + // lengthen the next calculation + pp=pp*2; // double precision + if (pp>p) pp=p; // clamp to maximum + aset.digits=pp; // working context + bset.digits=pp+rhs->digits; // wider context + } // Newton's iteration + + #if DECCHECK + // just a sanity check; remove the test to show always + if (iterations>24) + printf("Ln iterations=%ld, status=%08lx, p=%ld, d=%ld\n", + (LI)iterations, (LI)*status, (LI)p, (LI)rhs->digits); + #endif + + // Copy and round the result to res + residue=1; // indicate dirt to right + if (ISZERO(a)) residue=0; // .. unless underflowed to 0 + aset.digits=set->digits; // [use default rounding] + decCopyFit(res, a, &aset, &residue, status); // copy & shorten + decFinish(res, set, &residue, status); // cleanup/set flags + } while(0); // end protected + + if (allocbufa!=NULL) free(allocbufa); // drop any storage used + if (allocbufb!=NULL) free(allocbufb); // .. + // [status is handled by caller] + return res; + } // decLnOp + +/* ------------------------------------------------------------------ */ +/* decQuantizeOp -- force exponent to requested value */ +/* */ +/* This computes C = op(A, B), where op adjusts the coefficient */ +/* of C (by rounding or shifting) such that the exponent (-scale) */ +/* of C has the value B or matches the exponent of B. */ +/* The numerical value of C will equal A, except for the effects of */ +/* any rounding that occurred. */ +/* */ +/* res is C, the result. C may be A or B */ +/* lhs is A, the number to adjust */ +/* rhs is B, the requested exponent */ +/* set is the context */ +/* quant is 1 for quantize or 0 for rescale */ +/* status is the status accumulator (this can be called without */ +/* risk of control loss) */ +/* */ +/* C must have space for set->digits digits. */ +/* */ +/* Unless there is an error or the result is infinite, the exponent */ +/* after the operation is guaranteed to be that requested. */ +/* ------------------------------------------------------------------ */ +static decNumber * decQuantizeOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + Flag quant, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated + decNumber *allocrhs=NULL; // .., rhs + #endif + const decNumber *inrhs=rhs; // save original rhs + Int reqdigits=set->digits; // requested DIGITS + Int reqexp; // requested exponent [-scale] + Int residue=0; // rounding residue + Int etiny=set->emin-(reqdigits-1); + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operands and set lostDigits status, as needed + if (lhs->digits>reqdigits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==NULL) break; + lhs=alloclhs; + } + if (rhs->digits>reqdigits) { // [this only checks lostDigits] + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==NULL) break; + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + // Handle special values + if (SPECIALARGS) { + // NaNs get usual processing + if (SPECIALARGS & (DECSNAN | DECNAN)) + decNaNs(res, lhs, rhs, set, status); + // one infinity but not both is bad + else if ((lhs->bits ^ rhs->bits) & DECINF) + *status|=DEC_Invalid_operation; + // both infinity: return lhs + else decNumberCopy(res, lhs); // [nop if in place] + break; + } + + // set requested exponent + if (quant) reqexp=inrhs->exponent; // quantize -- match exponents + else { // rescale -- use value of rhs + // Original rhs must be an integer that fits and is in range, + // which could be from -1999999997 to +999999999, thanks to + // subnormals + reqexp=decGetInt(inrhs); // [cannot fail] + } + + #if DECSUBSET + if (!set->extended) etiny=set->emin; // no subnormals + #endif + + if (reqexp==BADINT // bad (rescale only) or .. + || reqexp==BIGODD || reqexp==BIGEVEN // very big (ditto) or .. + || (reqexpset->emax)) { // > emax + *status|=DEC_Invalid_operation; + break;} + + // the RHS has been processed, so it can be overwritten now if necessary + if (ISZERO(lhs)) { // zero coefficient unchanged + decNumberCopy(res, lhs); // [nop if in place] + res->exponent=reqexp; // .. just set exponent + #if DECSUBSET + if (!set->extended) res->bits=0; // subset specification; no -0 + #endif + } + else { // non-zero lhs + Int adjust=reqexp-lhs->exponent; // digit adjustment needed + // if adjusted coefficient will definitely not fit, give up now + if ((lhs->digits-adjust)>reqdigits) { + *status|=DEC_Invalid_operation; + break; + } + + if (adjust>0) { // increasing exponent + // this will decrease the length of the coefficient by adjust + // digits, and must round as it does so + decContext workset; // work + workset=*set; // clone rounding, etc. + workset.digits=lhs->digits-adjust; // set requested length + // [note that the latter can be <1, here] + decCopyFit(res, lhs, &workset, &residue, status); // fit to result + decApplyRound(res, &workset, residue, status); // .. and round + residue=0; // [used] + // If just rounded a 999s case, exponent will be off by one; + // adjust back (after checking space), if so. + if (res->exponent>reqexp) { + // re-check needed, e.g., for quantize(0.9999, 0.001) under + // set->digits==3 + if (res->digits==reqdigits) { // cannot shift by 1 + *status&=~(DEC_Inexact | DEC_Rounded); // [clean these] + *status|=DEC_Invalid_operation; + break; + } + res->digits=decShiftToMost(res->lsu, res->digits, 1); // shift + res->exponent--; // (re)adjust the exponent. + } + #if DECSUBSET + if (ISZERO(res) && !set->extended) res->bits=0; // subset; no -0 + #endif + } // increase + else /* adjust<=0 */ { // decreasing or = exponent + // this will increase the length of the coefficient by -adjust + // digits, by adding zero or more trailing zeros; this is + // already checked for fit, above + decNumberCopy(res, lhs); // [it will fit] + // if padding needed (adjust<0), add it now... + if (adjust<0) { + res->digits=decShiftToMost(res->lsu, res->digits, -adjust); + res->exponent+=adjust; // adjust the exponent + } + } // decrease + } // non-zero + + // Check for overflow [do not use Finalize in this case, as an + // overflow here is a "don't fit" situation] + if (res->exponent>set->emax-res->digits+1) { // too big + *status|=DEC_Invalid_operation; + break; + } + else { + decFinalize(res, set, &residue, status); // set subnormal flags + *status&=~DEC_Underflow; // suppress Underflow [as per 754] + } + } while(0); // end protected + + #if DECSUBSET + if (allocrhs!=NULL) free(allocrhs); // drop any storage used + if (alloclhs!=NULL) free(alloclhs); // .. + #endif + return res; + } // decQuantizeOp + +/* ------------------------------------------------------------------ */ +/* decCompareOp -- compare, min, or max two Numbers */ +/* */ +/* This computes C = A ? B and carries out one of four operations: */ +/* COMPARE -- returns the signum (as a number) giving the */ +/* result of a comparison unless one or both */ +/* operands is a NaN (in which case a NaN results) */ +/* COMPSIG -- as COMPARE except that a quiet NaN raises */ +/* Invalid operation. */ +/* COMPMAX -- returns the larger of the operands, using the */ +/* 754 maxnum operation */ +/* COMPMAXMAG -- ditto, comparing absolute values */ +/* COMPMIN -- the 754 minnum operation */ +/* COMPMINMAG -- ditto, comparing absolute values */ +/* COMTOTAL -- returns the signum (as a number) giving the */ +/* result of a comparison using 754 total ordering */ +/* */ +/* res is C, the result. C may be A and/or B (e.g., X=X?X) */ +/* lhs is A */ +/* rhs is B */ +/* set is the context */ +/* op is the operation flag */ +/* status is the usual accumulator */ +/* */ +/* C must have space for one digit for COMPARE or set->digits for */ +/* COMPMAX, COMPMIN, COMPMAXMAG, or COMPMINMAG. */ +/* ------------------------------------------------------------------ */ +/* The emphasis here is on speed for common cases, and avoiding */ +/* coefficient comparison if possible. */ +/* ------------------------------------------------------------------ */ +decNumber * decCompareOp(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + Flag op, uInt *status) { + #if DECSUBSET + decNumber *alloclhs=NULL; // non-NULL if rounded lhs allocated + decNumber *allocrhs=NULL; // .., rhs + #endif + Int result=0; // default result value + uByte merged; // work + + #if DECCHECK + if (decCheckOperands(res, lhs, rhs, set)) return res; + #endif + + do { // protect allocated storage + #if DECSUBSET + if (!set->extended) { + // reduce operands and set lostDigits status, as needed + if (lhs->digits>set->digits) { + alloclhs=decRoundOperand(lhs, set, status); + if (alloclhs==NULL) {result=BADINT; break;} + lhs=alloclhs; + } + if (rhs->digits>set->digits) { + allocrhs=decRoundOperand(rhs, set, status); + if (allocrhs==NULL) {result=BADINT; break;} + rhs=allocrhs; + } + } + #endif + // [following code does not require input rounding] + + // If total ordering then handle differing signs 'up front' + if (op==COMPTOTAL) { // total ordering + if (decNumberIsNegative(lhs) & !decNumberIsNegative(rhs)) { + result=-1; + break; + } + if (!decNumberIsNegative(lhs) & decNumberIsNegative(rhs)) { + result=+1; + break; + } + } + + // handle NaNs specially; let infinities drop through + // This assumes sNaN (even just one) leads to NaN. + merged=(lhs->bits | rhs->bits) & (DECSNAN | DECNAN); + if (merged) { // a NaN bit set + if (op==COMPARE); // result will be NaN + else if (op==COMPSIG) // treat qNaN as sNaN + *status|=DEC_Invalid_operation | DEC_sNaN; + else if (op==COMPTOTAL) { // total ordering, always finite + // signs are known to be the same; compute the ordering here + // as if the signs are both positive, then invert for negatives + if (!decNumberIsNaN(lhs)) result=-1; + else if (!decNumberIsNaN(rhs)) result=+1; + // here if both NaNs + else if (decNumberIsSNaN(lhs) && decNumberIsQNaN(rhs)) result=-1; + else if (decNumberIsQNaN(lhs) && decNumberIsSNaN(rhs)) result=+1; + else { // both NaN or both sNaN + // now it just depends on the payload + result=decUnitCompare(lhs->lsu, D2U(lhs->digits), + rhs->lsu, D2U(rhs->digits), 0); + // [Error not possible, as these are 'aligned'] + } // both same NaNs + if (decNumberIsNegative(lhs)) result=-result; + break; + } // total order + + else if (merged & DECSNAN); // sNaN -> qNaN + else { // here if MIN or MAX and one or two quiet NaNs + // min or max -- 754 rules ignore single NaN + if (!decNumberIsNaN(lhs) || !decNumberIsNaN(rhs)) { + // just one NaN; force choice to be the non-NaN operand + op=COMPMAX; + if (lhs->bits & DECNAN) result=-1; // pick rhs + else result=+1; // pick lhs + break; + } + } // max or min + op=COMPNAN; // use special path + decNaNs(res, lhs, rhs, set, status); // propagate NaN + break; + } + // have numbers + if (op==COMPMAXMAG || op==COMPMINMAG) result=decCompare(lhs, rhs, 1); + else result=decCompare(lhs, rhs, 0); // sign matters + } while(0); // end protected + + if (result==BADINT) *status|=DEC_Insufficient_storage; // rare + else { + if (op==COMPARE || op==COMPSIG ||op==COMPTOTAL) { // returning signum + if (op==COMPTOTAL && result==0) { + // operands are numerically equal or same NaN (and same sign, + // tested first); if identical, leave result 0 + if (lhs->exponent!=rhs->exponent) { + if (lhs->exponentexponent) result=-1; + else result=+1; + if (decNumberIsNegative(lhs)) result=-result; + } // lexp!=rexp + } // total-order by exponent + decNumberZero(res); // [always a valid result] + if (result!=0) { // must be -1 or +1 + *res->lsu=1; + if (result<0) res->bits=DECNEG; + } + } + else if (op==COMPNAN); // special, drop through + else { // MAX or MIN, non-NaN result + Int residue=0; // rounding accumulator + // choose the operand for the result + const decNumber *choice; + if (result==0) { // operands are numerically equal + // choose according to sign then exponent (see 754) + uByte slhs=(lhs->bits & DECNEG); + uByte srhs=(rhs->bits & DECNEG); + #if DECSUBSET + if (!set->extended) { // subset: force left-hand + op=COMPMAX; + result=+1; + } + else + #endif + if (slhs!=srhs) { // signs differ + if (slhs) result=-1; // rhs is max + else result=+1; // lhs is max + } + else if (slhs && srhs) { // both negative + if (lhs->exponentexponent) result=+1; + else result=-1; + // [if equal, use lhs, technically identical] + } + else { // both positive + if (lhs->exponent>rhs->exponent) result=+1; + else result=-1; + // [ditto] + } + } // numerically equal + // here result will be non-0; reverse if looking for MIN + if (op==COMPMIN || op==COMPMINMAG) result=-result; + choice=(result>0 ? lhs : rhs); // choose + // copy chosen to result, rounding if need be + decCopyFit(res, choice, set, &residue, status); + decFinish(res, set, &residue, status); + } + } + #if DECSUBSET + if (allocrhs!=NULL) free(allocrhs); // free any storage used + if (alloclhs!=NULL) free(alloclhs); // .. + #endif + return res; + } // decCompareOp + +/* ------------------------------------------------------------------ */ +/* decCompare -- compare two decNumbers by numerical value */ +/* */ +/* This routine compares A ? B without altering them. */ +/* */ +/* Arg1 is A, a decNumber which is not a NaN */ +/* Arg2 is B, a decNumber which is not a NaN */ +/* Arg3 is 1 for a sign-independent compare, 0 otherwise */ +/* */ +/* returns -1, 0, or 1 for AB, or BADINT if failure */ +/* (the only possible failure is an allocation error) */ +/* ------------------------------------------------------------------ */ +static Int decCompare(const decNumber *lhs, const decNumber *rhs, + Flag abs) { + Int result; // result value + Int sigr; // rhs signum + Int compare; // work + + result=1; // assume signum(lhs) + if (ISZERO(lhs)) result=0; + if (abs) { + if (ISZERO(rhs)) return result; // LHS wins or both 0 + // RHS is non-zero + if (result==0) return -1; // LHS is 0; RHS wins + // [here, both non-zero, result=1] + } + else { // signs matter + if (result && decNumberIsNegative(lhs)) result=-1; + sigr=1; // compute signum(rhs) + if (ISZERO(rhs)) sigr=0; + else if (decNumberIsNegative(rhs)) sigr=-1; + if (result > sigr) return +1; // L > R, return 1 + if (result < sigr) return -1; // L < R, return -1 + if (result==0) return 0; // both 0 + } + + // signums are the same; both are non-zero + if ((lhs->bits | rhs->bits) & DECINF) { // one or more infinities + if (decNumberIsInfinite(rhs)) { + if (decNumberIsInfinite(lhs)) result=0;// both infinite + else result=-result; // only rhs infinite + } + return result; + } + // must compare the coefficients, allowing for exponents + if (lhs->exponent>rhs->exponent) { // LHS exponent larger + // swap sides, and sign + const decNumber *temp=lhs; + lhs=rhs; + rhs=temp; + result=-result; + } + compare=decUnitCompare(lhs->lsu, D2U(lhs->digits), + rhs->lsu, D2U(rhs->digits), + rhs->exponent-lhs->exponent); + if (compare!=BADINT) compare*=result; // comparison succeeded + return compare; + } // decCompare + +/* ------------------------------------------------------------------ */ +/* decUnitCompare -- compare two >=0 integers in Unit arrays */ +/* */ +/* This routine compares A ? B*10**E where A and B are unit arrays */ +/* A is a plain integer */ +/* B has an exponent of E (which must be non-negative) */ +/* */ +/* Arg1 is A first Unit (lsu) */ +/* Arg2 is A length in Units */ +/* Arg3 is B first Unit (lsu) */ +/* Arg4 is B length in Units */ +/* Arg5 is E (0 if the units are aligned) */ +/* */ +/* returns -1, 0, or 1 for AB, or BADINT if failure */ +/* (the only possible failure is an allocation error, which can */ +/* only occur if E!=0) */ +/* ------------------------------------------------------------------ */ +static Int decUnitCompare(const Unit *a, Int alength, + const Unit *b, Int blength, Int exp) { + Unit *acc; // accumulator for result + Unit accbuff[SD2U(DECBUFFER*2+1)]; // local buffer + Unit *allocacc=NULL; // -> allocated acc buffer, iff allocated + Int accunits, need; // units in use or needed for acc + const Unit *l, *r, *u; // work + Int expunits, exprem, result; // .. + + if (exp==0) { // aligned; fastpath + if (alength>blength) return 1; + if (alength=a; l--, r--) { + if (*l>*r) return 1; + if (*l<*r) return -1; + } + return 0; // all units match + } // aligned + + // Unaligned. If one is >1 unit longer than the other, padded + // approximately, then can return easily + if (alength>blength+(Int)D2U(exp)) return 1; + if (alength+1sizeof(accbuff)) { + allocacc=(Unit *)malloc(need*sizeof(Unit)); + if (allocacc==NULL) return BADINT; // hopeless -- abandon + acc=allocacc; + } + // Calculate units and remainder from exponent. + expunits=exp/DECDPUN; + exprem=exp%DECDPUN; + // subtract [A+B*(-m)] + accunits=decUnitAddSub(a, alength, b, blength, expunits, acc, + -(Int)powers[exprem]); + // [UnitAddSub result may have leading zeros, even on zero] + if (accunits<0) result=-1; // negative result + else { // non-negative result + // check units of the result before freeing any storage + for (u=acc; u=0 integers in Unit arrays */ +/* */ +/* This routine performs the calculation: */ +/* */ +/* C=A+(B*M) */ +/* */ +/* Where M is in the range -DECDPUNMAX through +DECDPUNMAX. */ +/* */ +/* A may be shorter or longer than B. */ +/* */ +/* Leading zeros are not removed after a calculation. The result is */ +/* either the same length as the longer of A and B (adding any */ +/* shift), or one Unit longer than that (if a Unit carry occurred). */ +/* */ +/* A and B content are not altered unless C is also A or B. */ +/* C may be the same array as A or B, but only if no zero padding is */ +/* requested (that is, C may be B only if bshift==0). */ +/* C is filled from the lsu; only those units necessary to complete */ +/* the calculation are referenced. */ +/* */ +/* Arg1 is A first Unit (lsu) */ +/* Arg2 is A length in Units */ +/* Arg3 is B first Unit (lsu) */ +/* Arg4 is B length in Units */ +/* Arg5 is B shift in Units (>=0; pads with 0 units if positive) */ +/* Arg6 is C first Unit (lsu) */ +/* Arg7 is M, the multiplier */ +/* */ +/* returns the count of Units written to C, which will be non-zero */ +/* and negated if the result is negative. That is, the sign of the */ +/* returned Int is the sign of the result (positive for zero) and */ +/* the absolute value of the Int is the count of Units. */ +/* */ +/* It is the caller's responsibility to make sure that C size is */ +/* safe, allowing space if necessary for a one-Unit carry. */ +/* */ +/* This routine is severely performance-critical; *any* change here */ +/* must be measured (timed) to assure no performance degradation. */ +/* In particular, trickery here tends to be counter-productive, as */ +/* increased complexity of code hurts register optimizations on */ +/* register-poor architectures. Avoiding divisions is nearly */ +/* always a Good Idea, however. */ +/* */ +/* Special thanks to Rick McGuire (IBM Cambridge, MA) and Dave Clark */ +/* (IBM Warwick, UK) for some of the ideas used in this routine. */ +/* ------------------------------------------------------------------ */ +static Int decUnitAddSub(const Unit *a, Int alength, + const Unit *b, Int blength, Int bshift, + Unit *c, Int m) { + const Unit *alsu=a; // A lsu [need to remember it] + Unit *clsu=c; // C ditto + Unit *minC; // low water mark for C + Unit *maxC; // high water mark for C + eInt carry=0; // carry integer (could be Long) + Int add; // work + #if DECDPUN<=4 // myriadal, millenary, etc. + Int est; // estimated quotient + #endif + + #if DECTRACE + if (alength<1 || blength<1) + printf("decUnitAddSub: alen blen m %ld %ld [%ld]\n", alength, blength, m); + #endif + + maxC=c+alength; // A is usually the longer + minC=c+blength; // .. and B the shorter + if (bshift!=0) { // B is shifted; low As copy across + minC+=bshift; + // if in place [common], skip copy unless there's a gap [rare] + if (a==c && bshift<=alength) { + c+=bshift; + a+=bshift; + } + else for (; cmaxC) { // swap + Unit *hold=minC; + minC=maxC; + maxC=hold; + } + + // For speed, do the addition as two loops; the first where both A + // and B contribute, and the second (if necessary) where only one or + // other of the numbers contribute. + // Carry handling is the same (i.e., duplicated) in each case. + for (; c=0) { + est=(((ueInt)carry>>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder + carry=est; // likely quotient [89%] + if (*c>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); // correctly negative + if (*c=0) { + est=(((ueInt)carry>>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder + carry=est; // likely quotient [99%] + if (*c>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); // correctly negative + if (*c=0) { + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder + carry=est; // quotient + continue; + } + // negative case + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); // correctly negative + #else + // remainder operator is undefined if negative, so must test + if ((ueInt)carry<(DECDPUNMAX+1)*2) { // fastpath carry +1 + *c=(Unit)(carry-(DECDPUNMAX+1)); // [helps additions] + carry=1; + continue; + } + if (carry>=0) { + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1); + continue; + } + // negative case + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1); + #endif + } // c + + // now may have one or other to complete + // [pretest to avoid loop setup/shutdown] + if (cDECDPUNMAX + #if DECDPUN==4 // use divide-by-multiply + if (carry>=0) { + est=(((ueInt)carry>>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder + carry=est; // likely quotient [79.7%] + if (*c>11)*53687)>>18; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); // correctly negative + if (*c=0) { + est=(((ueInt)carry>>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder + carry=est; // likely quotient [99%] + if (*c>3)*16777)>>21; + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); // correctly negative + if (*c=0) { + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); // remainder + carry=est; // quotient + continue; + } + // negative case + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive + est=QUOT10(carry, DECDPUN); + *c=(Unit)(carry-est*(DECDPUNMAX+1)); + carry=est-(DECDPUNMAX+1); // correctly negative + #else + if ((ueInt)carry<(DECDPUNMAX+1)*2){ // fastpath carry 1 + *c=(Unit)(carry-(DECDPUNMAX+1)); + carry=1; + continue; + } + // remainder operator is undefined if negative, so must test + if (carry>=0) { + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1); + continue; + } + // negative case + carry=carry+(eInt)(DECDPUNMAX+1)*(DECDPUNMAX+1); // make positive + *c=(Unit)(carry%(DECDPUNMAX+1)); + carry=carry/(DECDPUNMAX+1)-(DECDPUNMAX+1); + #endif + } // c + + // OK, all A and B processed; might still have carry or borrow + // return number of Units in the result, negated if a borrow + if (carry==0) return c-clsu; // no carry, so no more to do + if (carry>0) { // positive carry + *c=(Unit)carry; // place as new unit + c++; // .. + return c-clsu; + } + // -ve carry: it's a borrow; complement needed + add=1; // temporary carry... + for (c=clsu; c current Unit + + #if DECCHECK + if (decCheckOperands(dn, DECUNUSED, DECUNUSED, DECUNCONT)) return dn; + #endif + + *dropped=0; // assume no zeros dropped + if ((dn->bits & DECSPECIAL) // fast exit if special .. + || (*dn->lsu & 0x01)) return dn; // .. or odd + if (ISZERO(dn)) { // .. or 0 + dn->exponent=0; // (sign is preserved) + return dn; + } + + // have a finite number which is even + exp=dn->exponent; + cut=1; // digit (1-DECDPUN) in Unit + up=dn->lsu; // -> current Unit + for (d=0; ddigits-1; d++) { // [don't strip the final digit] + // slice by powers + #if DECDPUN<=4 + uInt quot=QUOT10(*up, cut); + if ((*up-quot*powers[cut])!=0) break; // found non-0 digit + #else + if (*up%powers[cut]!=0) break; // found non-0 digit + #endif + // have a trailing 0 + if (!all) { // trimming + // [if exp>0 then all trailing 0s are significant for trim] + if (exp<=0) { // if digit might be significant + if (exp==0) break; // then quit + exp++; // next digit might be significant + } + } + cut++; // next power + if (cut>DECDPUN) { // need new Unit + up++; + cut=1; + } + } // d + if (d==0) return dn; // none to drop + + // may need to limit drop if clamping + if (set->clamp && !noclamp) { + Int maxd=set->emax-set->digits+1-dn->exponent; + if (maxd<=0) return dn; // nothing possible + if (d>maxd) d=maxd; + } + + // effect the drop + decShiftToLeast(dn->lsu, D2U(dn->digits), d); + dn->exponent+=d; // maintain numerical value + dn->digits-=d; // new length + *dropped=d; // report the count + return dn; + } // decTrim + +/* ------------------------------------------------------------------ */ +/* decReverse -- reverse a Unit array in place */ +/* */ +/* ulo is the start of the array */ +/* uhi is the end of the array (highest Unit to include) */ +/* */ +/* The units ulo through uhi are reversed in place (if the number */ +/* of units is odd, the middle one is untouched). Note that the */ +/* digit(s) in each unit are unaffected. */ +/* ------------------------------------------------------------------ */ +static void decReverse(Unit *ulo, Unit *uhi) { + Unit temp; + for (; ulo=uar; source--, target--) *target=*source; + } + else { + first=uar+D2U(digits+shift)-1; // where msu of source will end up + for (; source>=uar; source--, target--) { + // split the source Unit and accumulate remainder for next + #if DECDPUN<=4 + uInt quot=QUOT10(*source, cut); + uInt rem=*source-quot*powers[cut]; + next+=quot; + #else + uInt rem=*source%powers[cut]; + next+=*source/powers[cut]; + #endif + if (target<=first) *target=(Unit)next; // write to target iff valid + next=rem*powers[DECDPUN-cut]; // save remainder for next Unit + } + } // shift-move + + // propagate any partial unit to one below and clear the rest + for (; target>=uar; target--) { + *target=(Unit)next; + next=0; + } + return digits+shift; + } // decShiftToMost + +/* ------------------------------------------------------------------ */ +/* decShiftToLeast -- shift digits in array towards least significant */ +/* */ +/* uar is the array */ +/* units is length of the array, in units */ +/* shift is the number of digits to remove from the lsu end; it */ +/* must be zero or positive and <= than units*DECDPUN. */ +/* */ +/* returns the new length of the integer in the array, in units */ +/* */ +/* Removed digits are discarded (lost). Units not required to hold */ +/* the final result are unchanged. */ +/* ------------------------------------------------------------------ */ +static Int decShiftToLeast(Unit *uar, Int units, Int shift) { + Unit *target, *up; // work + Int cut, count; // work + Int quot, rem; // for division + + if (shift==0) return units; // [fastpath] nothing to do + if (shift==units*DECDPUN) { // [fastpath] little to do + *uar=0; // all digits cleared gives zero + return 1; // leaves just the one + } + + target=uar; // both paths + cut=MSUDIGITS(shift); + if (cut==DECDPUN) { // unit-boundary case; easy + up=uar+D2U(shift); + for (; updigits is > set->digits) */ +/* set is the relevant context */ +/* status is the status accumulator */ +/* */ +/* returns an allocated decNumber with the rounded result. */ +/* */ +/* lostDigits and other status may be set by this. */ +/* */ +/* Since the input is an operand, it must not be modified. */ +/* Instead, return an allocated decNumber, rounded as required. */ +/* It is the caller's responsibility to free the allocated storage. */ +/* */ +/* If no storage is available then the result cannot be used, so NULL */ +/* is returned. */ +/* ------------------------------------------------------------------ */ +static decNumber *decRoundOperand(const decNumber *dn, decContext *set, + uInt *status) { + decNumber *res; // result structure + uInt newstatus=0; // status from round + Int residue=0; // rounding accumulator + + // Allocate storage for the returned decNumber, big enough for the + // length specified by the context + res=(decNumber *)malloc(sizeof(decNumber) + +(D2U(set->digits)-1)*sizeof(Unit)); + if (res==NULL) { + *status|=DEC_Insufficient_storage; + return NULL; + } + decCopyFit(res, dn, set, &residue, &newstatus); + decApplyRound(res, set, residue, &newstatus); + + // If that set Inexact then "lost digits" is raised... + if (newstatus & DEC_Inexact) newstatus|=DEC_Lost_digits; + *status|=newstatus; + return res; + } // decRoundOperand +#endif + +/* ------------------------------------------------------------------ */ +/* decCopyFit -- copy a number, truncating the coefficient if needed */ +/* */ +/* dest is the target decNumber */ +/* src is the source decNumber */ +/* set is the context [used for length (digits) and rounding mode] */ +/* residue is the residue accumulator */ +/* status contains the current status to be updated */ +/* */ +/* (dest==src is allowed and will be a no-op if fits) */ +/* All fields are updated as required. */ +/* ------------------------------------------------------------------ */ +static void decCopyFit(decNumber *dest, const decNumber *src, + decContext *set, Int *residue, uInt *status) { + dest->bits=src->bits; + dest->exponent=src->exponent; + decSetCoeff(dest, set, src->lsu, src->digits, residue, status); + } // decCopyFit + +/* ------------------------------------------------------------------ */ +/* decSetCoeff -- set the coefficient of a number */ +/* */ +/* dn is the number whose coefficient array is to be set. */ +/* It must have space for set->digits digits */ +/* set is the context [for size] */ +/* lsu -> lsu of the source coefficient [may be dn->lsu] */ +/* len is digits in the source coefficient [may be dn->digits] */ +/* residue is the residue accumulator. This has values as in */ +/* decApplyRound, and will be unchanged unless the */ +/* target size is less than len. In this case, the */ +/* coefficient is truncated and the residue is updated to */ +/* reflect the previous residue and the dropped digits. */ +/* status is the status accumulator, as usual */ +/* */ +/* The coefficient may already be in the number, or it can be an */ +/* external intermediate array. If it is in the number, lsu must == */ +/* dn->lsu and len must == dn->digits. */ +/* */ +/* Note that the coefficient length (len) may be < set->digits, and */ +/* in this case this merely copies the coefficient (or is a no-op */ +/* if dn->lsu==lsu). */ +/* */ +/* Note also that (only internally, from decQuantizeOp and */ +/* decSetSubnormal) the value of set->digits may be less than one, */ +/* indicating a round to left. This routine handles that case */ +/* correctly; caller ensures space. */ +/* */ +/* dn->digits, dn->lsu (and as required), and dn->exponent are */ +/* updated as necessary. dn->bits (sign) is unchanged. */ +/* */ +/* DEC_Rounded status is set if any digits are discarded. */ +/* DEC_Inexact status is set if any non-zero digits are discarded, or */ +/* incoming residue was non-0 (implies rounded) */ +/* ------------------------------------------------------------------ */ +// mapping array: maps 0-9 to canonical residues, so that a residue +// can be adjusted in the range [-1, +1] and achieve correct rounding +// 0 1 2 3 4 5 6 7 8 9 +static const uByte resmap[10]={0, 3, 3, 3, 3, 5, 7, 7, 7, 7}; +static void decSetCoeff(decNumber *dn, decContext *set, const Unit *lsu, + Int len, Int *residue, uInt *status) { + Int discard; // number of digits to discard + uInt cut; // cut point in Unit + const Unit *up; // work + Unit *target; // .. + Int count; // .. + #if DECDPUN<=4 + uInt temp; // .. + #endif + + discard=len-set->digits; // digits to discard + if (discard<=0) { // no digits are being discarded + if (dn->lsu!=lsu) { // copy needed + // copy the coefficient array to the result number; no shift needed + count=len; // avoids D2U + up=lsu; + for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN) + *target=*up; + dn->digits=len; // set the new length + } + // dn->exponent and residue are unchanged, record any inexactitude + if (*residue!=0) *status|=(DEC_Inexact | DEC_Rounded); + return; + } + + // some digits must be discarded ... + dn->exponent+=discard; // maintain numerical value + *status|=DEC_Rounded; // accumulate Rounded status + if (*residue>1) *residue=1; // previous residue now to right, so reduce + + if (discard>len) { // everything, +1, is being discarded + // guard digit is 0 + // residue is all the number [NB could be all 0s] + if (*residue<=0) { // not already positive + count=len; // avoids D2U + for (up=lsu; count>0; up++, count-=DECDPUN) if (*up!=0) { // found non-0 + *residue=1; + break; // no need to check any others + } + } + if (*residue!=0) *status|=DEC_Inexact; // record inexactitude + *dn->lsu=0; // coefficient will now be 0 + dn->digits=1; // .. + return; + } // total discard + + // partial discard [most common case] + // here, at least the first (most significant) discarded digit exists + + // spin up the number, noting residue during the spin, until get to + // the Unit with the first discarded digit. When reach it, extract + // it and remember its position + count=0; + for (up=lsu;; up++) { + count+=DECDPUN; + if (count>=discard) break; // full ones all checked + if (*up!=0) *residue=1; + } // up + + // here up -> Unit with first discarded digit + cut=discard-(count-DECDPUN)-1; + if (cut==DECDPUN-1) { // unit-boundary case (fast) + Unit half=(Unit)powers[DECDPUN]>>1; + // set residue directly + if (*up>=half) { + if (*up>half) *residue=7; + else *residue+=5; // add sticky bit + } + else { // digits<=0) { // special for Quantize/Subnormal :-( + *dn->lsu=0; // .. result is 0 + dn->digits=1; // .. + } + else { // shift to least + count=set->digits; // now digits to end up with + dn->digits=count; // set the new length + up++; // move to next + // on unit boundary, so shift-down copy loop is simple + for (target=dn->lsu; count>0; target++, up++, count-=DECDPUN) + *target=*up; + } + } // unit-boundary case + + else { // discard digit is in low digit(s), and not top digit + uInt discard1; // first discarded digit + uInt quot, rem; // for divisions + if (cut==0) quot=*up; // is at bottom of unit + else /* cut>0 */ { // it's not at bottom of unit + #if DECDPUN<=4 + quot=QUOT10(*up, cut); + rem=*up-quot*powers[cut]; + #else + rem=*up%powers[cut]; + quot=*up/powers[cut]; + #endif + if (rem!=0) *residue=1; + } + // discard digit is now at bottom of quot + #if DECDPUN<=4 + temp=(quot*6554)>>16; // fast /10 + // Vowels algorithm here not a win (9 instructions) + discard1=quot-X10(temp); + quot=temp; + #else + discard1=quot%10; + quot=quot/10; + #endif + // here, discard1 is the guard digit, and residue is everything + // else [use mapping array to accumulate residue safely] + *residue+=resmap[discard1]; + cut++; // update cut + // here: up -> Unit of the array with bottom digit + // cut is the division point for each Unit + // quot holds the uncut high-order digits for the current unit + if (set->digits<=0) { // special for Quantize/Subnormal :-( + *dn->lsu=0; // .. result is 0 + dn->digits=1; // .. + } + else { // shift to least needed + count=set->digits; // now digits to end up with + dn->digits=count; // set the new length + // shift-copy the coefficient array to the result number + for (target=dn->lsu; ; target++) { + *target=(Unit)quot; + count-=(DECDPUN-cut); + if (count<=0) break; + up++; + quot=*up; + #if DECDPUN<=4 + quot=QUOT10(quot, cut); + rem=*up-quot*powers[cut]; + #else + rem=quot%powers[cut]; + quot=quot/powers[cut]; + #endif + *target=(Unit)(*target+rem*powers[DECDPUN-cut]); + count-=cut; + if (count<=0) break; + } // shift-copy loop + } // shift to least + } // not unit boundary + + if (*residue!=0) *status|=DEC_Inexact; // record inexactitude + return; + } // decSetCoeff + +/* ------------------------------------------------------------------ */ +/* decApplyRound -- apply pending rounding to a number */ +/* */ +/* dn is the number, with space for set->digits digits */ +/* set is the context [for size and rounding mode] */ +/* residue indicates pending rounding, being any accumulated */ +/* guard and sticky information. It may be: */ +/* 6-9: rounding digit is >5 */ +/* 5: rounding digit is exactly half-way */ +/* 1-4: rounding digit is <5 and >0 */ +/* 0: the coefficient is exact */ +/* -1: as 1, but the hidden digits are subtractive, that */ +/* is, of the opposite sign to dn. In this case the */ +/* coefficient must be non-0. This case occurs when */ +/* subtracting a small number (which can be reduced to */ +/* a sticky bit); see decAddOp. */ +/* status is the status accumulator, as usual */ +/* */ +/* This routine applies rounding while keeping the length of the */ +/* coefficient constant. The exponent and status are unchanged */ +/* except if: */ +/* */ +/* -- the coefficient was increased and is all nines (in which */ +/* case Overflow could occur, and is handled directly here so */ +/* the caller does not need to re-test for overflow) */ +/* */ +/* -- the coefficient was decreased and becomes all nines (in which */ +/* case Underflow could occur, and is also handled directly). */ +/* */ +/* All fields in dn are updated as required. */ +/* */ +/* ------------------------------------------------------------------ */ +static void decApplyRound(decNumber *dn, decContext *set, Int residue, + uInt *status) { + Int bump; // 1 if coefficient needs to be incremented + // -1 if coefficient needs to be decremented + + if (residue==0) return; // nothing to apply + + bump=0; // assume a smooth ride + + // now decide whether, and how, to round, depending on mode + switch (set->round) { + case DEC_ROUND_05UP: { // round zero or five up (for reround) + // This is the same as DEC_ROUND_DOWN unless there is a + // positive residue and the lsd of dn is 0 or 5, in which case + // it is bumped; when residue is <0, the number is therefore + // bumped down unless the final digit was 1 or 6 (in which + // case it is bumped down and then up -- a no-op) + Int lsd5=*dn->lsu%5; // get lsd and quintate + if (residue<0 && lsd5!=1) bump=-1; + else if (residue>0 && lsd5==0) bump=1; + // [bump==1 could be applied directly; use common path for clarity] + break;} // r-05 + + case DEC_ROUND_DOWN: { + // no change, except if negative residue + if (residue<0) bump=-1; + break;} // r-d + + case DEC_ROUND_HALF_DOWN: { + if (residue>5) bump=1; + break;} // r-h-d + + case DEC_ROUND_HALF_EVEN: { + if (residue>5) bump=1; // >0.5 goes up + else if (residue==5) { // exactly 0.5000... + // 0.5 goes up iff [new] lsd is odd + if (*dn->lsu & 0x01) bump=1; + } + break;} // r-h-e + + case DEC_ROUND_HALF_UP: { + if (residue>=5) bump=1; + break;} // r-h-u + + case DEC_ROUND_UP: { + if (residue>0) bump=1; + break;} // r-u + + case DEC_ROUND_CEILING: { + // same as _UP for positive numbers, and as _DOWN for negatives + // [negative residue cannot occur on 0] + if (decNumberIsNegative(dn)) { + if (residue<0) bump=-1; + } + else { + if (residue>0) bump=1; + } + break;} // r-c + + case DEC_ROUND_FLOOR: { + // same as _UP for negative numbers, and as _DOWN for positive + // [negative residue cannot occur on 0] + if (!decNumberIsNegative(dn)) { + if (residue<0) bump=-1; + } + else { + if (residue>0) bump=1; + } + break;} // r-f + + default: { // e.g., DEC_ROUND_MAX + *status|=DEC_Invalid_context; + #if DECTRACE || (DECCHECK && DECVERB) + printf("Unknown rounding mode: %d\n", set->round); + #endif + break;} + } // switch + + // now bump the number, up or down, if need be + if (bump==0) return; // no action required + + // Simply use decUnitAddSub unless bumping up and the number is + // all nines. In this special case set to 100... explicitly + // and adjust the exponent by one (as otherwise could overflow + // the array) + // Similarly handle all-nines result if bumping down. + if (bump>0) { + Unit *up; // work + uInt count=dn->digits; // digits to be checked + for (up=dn->lsu; ; up++) { + if (count<=DECDPUN) { + // this is the last Unit (the msu) + if (*up!=powers[count]-1) break; // not still 9s + // here if it, too, is all nines + *up=(Unit)powers[count-1]; // here 999 -> 100 etc. + for (up=up-1; up>=dn->lsu; up--) *up=0; // others all to 0 + dn->exponent++; // and bump exponent + // [which, very rarely, could cause Overflow...] + if ((dn->exponent+dn->digits)>set->emax+1) { + decSetOverflow(dn, set, status); + } + return; // done + } + // a full unit to check, with more to come + if (*up!=DECDPUNMAX) break; // not still 9s + count-=DECDPUN; + } // up + } // bump>0 + else { // -1 + // here checking for a pre-bump of 1000... (leading 1, all + // other digits zero) + Unit *up, *sup; // work + uInt count=dn->digits; // digits to be checked + for (up=dn->lsu; ; up++) { + if (count<=DECDPUN) { + // this is the last Unit (the msu) + if (*up!=powers[count-1]) break; // not 100.. + // here if have the 1000... case + sup=up; // save msu pointer + *up=(Unit)powers[count]-1; // here 100 in msu -> 999 + // others all to all-nines, too + for (up=up-1; up>=dn->lsu; up--) *up=(Unit)powers[DECDPUN]-1; + dn->exponent--; // and bump exponent + + // iff the number was at the subnormal boundary (exponent=etiny) + // then the exponent is now out of range, so it will in fact get + // clamped to etiny and the final 9 dropped. + // printf(">> emin=%d exp=%d sdig=%d\n", set->emin, + // dn->exponent, set->digits); + if (dn->exponent+1==set->emin-set->digits+1) { + if (count==1 && dn->digits==1) *sup=0; // here 9 -> 0[.9] + else { + *sup=(Unit)powers[count-1]-1; // here 999.. in msu -> 99.. + dn->digits--; + } + dn->exponent++; + *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded; + } + return; // done + } + + // a full unit to check, with more to come + if (*up!=0) break; // not still 0s + count-=DECDPUN; + } // up + + } // bump<0 + + // Actual bump needed. Do it. + decUnitAddSub(dn->lsu, D2U(dn->digits), uarrone, 1, 0, dn->lsu, bump); + } // decApplyRound + +#if DECSUBSET +/* ------------------------------------------------------------------ */ +/* decFinish -- finish processing a number */ +/* */ +/* dn is the number */ +/* set is the context */ +/* residue is the rounding accumulator (as in decApplyRound) */ +/* status is the accumulator */ +/* */ +/* This finishes off the current number by: */ +/* 1. If not extended: */ +/* a. Converting a zero result to clean '0' */ +/* b. Reducing positive exponents to 0, if would fit in digits */ +/* 2. Checking for overflow and subnormals (always) */ +/* Note this is just Finalize when no subset arithmetic. */ +/* All fields are updated as required. */ +/* ------------------------------------------------------------------ */ +static void decFinish(decNumber *dn, decContext *set, Int *residue, + uInt *status) { + if (!set->extended) { + if ISZERO(dn) { // value is zero + dn->exponent=0; // clean exponent .. + dn->bits=0; // .. and sign + return; // no error possible + } + if (dn->exponent>=0) { // non-negative exponent + // >0; reduce to integer if possible + if (set->digits >= (dn->exponent+dn->digits)) { + dn->digits=decShiftToMost(dn->lsu, dn->digits, dn->exponent); + dn->exponent=0; + } + } + } // !extended + + decFinalize(dn, set, residue, status); + } // decFinish +#endif + +/* ------------------------------------------------------------------ */ +/* decFinalize -- final check, clamp, and round of a number */ +/* */ +/* dn is the number */ +/* set is the context */ +/* residue is the rounding accumulator (as in decApplyRound) */ +/* status is the status accumulator */ +/* */ +/* This finishes off the current number by checking for subnormal */ +/* results, applying any pending rounding, checking for overflow, */ +/* and applying any clamping. */ +/* Underflow and overflow conditions are raised as appropriate. */ +/* All fields are updated as required. */ +/* ------------------------------------------------------------------ */ +static void decFinalize(decNumber *dn, decContext *set, Int *residue, + uInt *status) { + Int shift; // shift needed if clamping + Int tinyexp=set->emin-dn->digits+1; // precalculate subnormal boundary + + // Must be careful, here, when checking the exponent as the + // adjusted exponent could overflow 31 bits [because it may already + // be up to twice the expected]. + + // First test for subnormal. This must be done before any final + // round as the result could be rounded to Nmin or 0. + if (dn->exponent<=tinyexp) { // prefilter + Int comp; + decNumber nmin; + // A very nasty case here is dn == Nmin and residue<0 + if (dn->exponentemin; + comp=decCompare(dn, &nmin, 1); // (signless compare) + if (comp==BADINT) { // oops + *status|=DEC_Insufficient_storage; // abandon... + return; + } + if (*residue<0 && comp==0) { // neg residue and dn==Nmin + decApplyRound(dn, set, *residue, status); // might force down + decSetSubnormal(dn, set, residue, status); + return; + } + } + + // now apply any pending round (this could raise overflow). + if (*residue!=0) decApplyRound(dn, set, *residue, status); + + // Check for overflow [redundant in the 'rare' case] or clamp + if (dn->exponent<=set->emax-set->digits+1) return; // neither needed + + + // here when might have an overflow or clamp to do + if (dn->exponent>set->emax-dn->digits+1) { // too big + decSetOverflow(dn, set, status); + return; + } + // here when the result is normal but in clamp range + if (!set->clamp) return; + + // here when need to apply the IEEE exponent clamp (fold-down) + shift=dn->exponent-(set->emax-set->digits+1); + + // shift coefficient (if non-zero) + if (!ISZERO(dn)) { + dn->digits=decShiftToMost(dn->lsu, dn->digits, shift); + } + dn->exponent-=shift; // adjust the exponent to match + *status|=DEC_Clamped; // and record the dirty deed + return; + } // decFinalize + +/* ------------------------------------------------------------------ */ +/* decSetOverflow -- set number to proper overflow value */ +/* */ +/* dn is the number (used for sign [only] and result) */ +/* set is the context [used for the rounding mode, etc.] */ +/* status contains the current status to be updated */ +/* */ +/* This sets the sign of a number and sets its value to either */ +/* Infinity or the maximum finite value, depending on the sign of */ +/* dn and the rounding mode, following IEEE 754 rules. */ +/* ------------------------------------------------------------------ */ +static void decSetOverflow(decNumber *dn, decContext *set, uInt *status) { + Flag needmax=0; // result is maximum finite value + uByte sign=dn->bits&DECNEG; // clean and save sign bit + + if (ISZERO(dn)) { // zero does not overflow magnitude + Int emax=set->emax; // limit value + if (set->clamp) emax-=set->digits-1; // lower if clamping + if (dn->exponent>emax) { // clamp required + dn->exponent=emax; + *status|=DEC_Clamped; + } + return; + } + + decNumberZero(dn); + switch (set->round) { + case DEC_ROUND_DOWN: { + needmax=1; // never Infinity + break;} // r-d + case DEC_ROUND_05UP: { + needmax=1; // never Infinity + break;} // r-05 + case DEC_ROUND_CEILING: { + if (sign) needmax=1; // Infinity if non-negative + break;} // r-c + case DEC_ROUND_FLOOR: { + if (!sign) needmax=1; // Infinity if negative + break;} // r-f + default: break; // Infinity in all other cases + } + if (needmax) { + decSetMaxValue(dn, set); + dn->bits=sign; // set sign + } + else dn->bits=sign|DECINF; // Value is +/-Infinity + *status|=DEC_Overflow | DEC_Inexact | DEC_Rounded; + } // decSetOverflow + +/* ------------------------------------------------------------------ */ +/* decSetMaxValue -- set number to +Nmax (maximum normal value) */ +/* */ +/* dn is the number to set */ +/* set is the context [used for digits and emax] */ +/* */ +/* This sets the number to the maximum positive value. */ +/* ------------------------------------------------------------------ */ +static void decSetMaxValue(decNumber *dn, decContext *set) { + Unit *up; // work + Int count=set->digits; // nines to add + dn->digits=count; + // fill in all nines to set maximum value + for (up=dn->lsu; ; up++) { + if (count>DECDPUN) *up=DECDPUNMAX; // unit full o'nines + else { // this is the msu + *up=(Unit)(powers[count]-1); + break; + } + count-=DECDPUN; // filled those digits + } // up + dn->bits=0; // + sign + dn->exponent=set->emax-set->digits+1; + } // decSetMaxValue + +/* ------------------------------------------------------------------ */ +/* decSetSubnormal -- process value whose exponent is extended) { + decNumberZero(dn); + // always full overflow + *status|=DEC_Underflow | DEC_Subnormal | DEC_Inexact | DEC_Rounded; + return; + } + #endif + + // Full arithmetic -- allow subnormals, rounded to minimum exponent + // (Etiny) if needed + etiny=set->emin-(set->digits-1); // smallest allowed exponent + + if ISZERO(dn) { // value is zero + // residue can never be non-zero here + #if DECCHECK + if (*residue!=0) { + printf("++ Subnormal 0 residue %ld\n", (LI)*residue); + *status|=DEC_Invalid_operation; + } + #endif + if (dn->exponentexponent=etiny; + *status|=DEC_Clamped; + } + return; + } + + *status|=DEC_Subnormal; // have a non-zero subnormal + adjust=etiny-dn->exponent; // calculate digits to remove + if (adjust<=0) { // not out of range; unrounded + // residue can never be non-zero here, except in the Nmin-residue + // case (which is a subnormal result), so can take fast-path here + // it may already be inexact (from setting the coefficient) + if (*status&DEC_Inexact) *status|=DEC_Underflow; + return; + } + + // adjust>0, so need to rescale the result so exponent becomes Etiny + // [this code is similar to that in rescale] + workset=*set; // clone rounding, etc. + workset.digits=dn->digits-adjust; // set requested length + workset.emin-=adjust; // and adjust emin to match + // [note that the latter can be <1, here, similar to Rescale case] + decSetCoeff(dn, &workset, dn->lsu, dn->digits, residue, status); + decApplyRound(dn, &workset, *residue, status); + + // Use 754 default rule: Underflow is set iff Inexact + // [independent of whether trapped] + if (*status&DEC_Inexact) *status|=DEC_Underflow; + + // if rounded up a 999s case, exponent will be off by one; adjust + // back if so [it will fit, because it was shortened earlier] + if (dn->exponent>etiny) { + dn->digits=decShiftToMost(dn->lsu, dn->digits, 1); + dn->exponent--; // (re)adjust the exponent. + } + + // if rounded to zero, it is by definition clamped... + if (ISZERO(dn)) *status|=DEC_Clamped; + } // decSetSubnormal + +/* ------------------------------------------------------------------ */ +/* decCheckMath - check entry conditions for a math function */ +/* */ +/* This checks the context and the operand */ +/* */ +/* rhs is the operand to check */ +/* set is the context to check */ +/* status is unchanged if both are good */ +/* */ +/* returns non-zero if status is changed, 0 otherwise */ +/* */ +/* Restrictions enforced: */ +/* */ +/* digits, emax, and -emin in the context must be less than */ +/* DEC_MAX_MATH (999999), and A must be within these bounds if */ +/* non-zero. Invalid_operation is set in the status if a */ +/* restriction is violated. */ +/* ------------------------------------------------------------------ */ +static uInt decCheckMath(const decNumber *rhs, decContext *set, + uInt *status) { + uInt save=*status; // record + if (set->digits>DEC_MAX_MATH + || set->emax>DEC_MAX_MATH + || -set->emin>DEC_MAX_MATH) *status|=DEC_Invalid_context; + else if ((rhs->digits>DEC_MAX_MATH + || rhs->exponent+rhs->digits>DEC_MAX_MATH+1 + || rhs->exponent+rhs->digits<2*(1-DEC_MAX_MATH)) + && !ISZERO(rhs)) *status|=DEC_Invalid_operation; + return (*status!=save); + } // decCheckMath + +/* ------------------------------------------------------------------ */ +/* decGetInt -- get integer from a number */ +/* */ +/* dn is the number [which will not be altered] */ +/* */ +/* returns one of: */ +/* BADINT if there is a non-zero fraction */ +/* the converted integer */ +/* BIGEVEN if the integer is even and magnitude > 2*10**9 */ +/* BIGODD if the integer is odd and magnitude > 2*10**9 */ +/* */ +/* This checks and gets a whole number from the input decNumber. */ +/* The sign can be determined from dn by the caller when BIGEVEN or */ +/* BIGODD is returned. */ +/* ------------------------------------------------------------------ */ +static Int decGetInt(const decNumber *dn) { + Int theInt; // result accumulator + const Unit *up; // work + Int got; // digits (real or not) processed + Int ilength=dn->digits+dn->exponent; // integral length + Flag neg=decNumberIsNegative(dn); // 1 if -ve + + // The number must be an integer that fits in 10 digits + // Assert, here, that 10 is enough for any rescale Etiny + #if DEC_MAX_EMAX > 999999999 + #error GetInt may need updating [for Emax] + #endif + #if DEC_MIN_EMIN < -999999999 + #error GetInt may need updating [for Emin] + #endif + if (ISZERO(dn)) return 0; // zeros are OK, with any exponent + + up=dn->lsu; // ready for lsu + theInt=0; // ready to accumulate + if (dn->exponent>=0) { // relatively easy + // no fractional part [usual]; allow for positive exponent + got=dn->exponent; + } + else { // -ve exponent; some fractional part to check and discard + Int count=-dn->exponent; // digits to discard + // spin up whole units until reach the Unit with the unit digit + for (; count>=DECDPUN; up++) { + if (*up!=0) return BADINT; // non-zero Unit to discard + count-=DECDPUN; + } + if (count==0) got=0; // [a multiple of DECDPUN] + else { // [not multiple of DECDPUN] + Int rem; // work + // slice off fraction digits and check for non-zero + #if DECDPUN<=4 + theInt=QUOT10(*up, count); + rem=*up-theInt*powers[count]; + #else + rem=*up%powers[count]; // slice off discards + theInt=*up/powers[count]; + #endif + if (rem!=0) return BADINT; // non-zero fraction + // it looks good + got=DECDPUN-count; // number of digits so far + up++; // ready for next + } + } + // now it's known there's no fractional part + + // tricky code now, to accumulate up to 9.3 digits + if (got==0) {theInt=*up; got+=DECDPUN; up++;} // ensure lsu is there + + if (ilength<11) { + Int save=theInt; + // collect any remaining unit(s) + for (; got1999999997) ilength=11; + else if (!neg && theInt>999999999) ilength=11; + if (ilength==11) theInt=save; // restore correct low bit + } + } + + if (ilength>10) { // too big + if (theInt&1) return BIGODD; // bottom bit 1 + return BIGEVEN; // bottom bit 0 + } + + if (neg) theInt=-theInt; // apply sign + return theInt; + } // decGetInt + +/* ------------------------------------------------------------------ */ +/* decDecap -- decapitate the coefficient of a number */ +/* */ +/* dn is the number to be decapitated */ +/* drop is the number of digits to be removed from the left of dn; */ +/* this must be <= dn->digits (if equal, the coefficient is */ +/* set to 0) */ +/* */ +/* Returns dn; dn->digits will be <= the initial digits less drop */ +/* (after removing drop digits there may be leading zero digits */ +/* which will also be removed). Only dn->lsu and dn->digits change. */ +/* ------------------------------------------------------------------ */ +static decNumber *decDecap(decNumber *dn, Int drop) { + Unit *msu; // -> target cut point + Int cut; // work + if (drop>=dn->digits) { // losing the whole thing + #if DECCHECK + if (drop>dn->digits) + printf("decDecap called with drop>digits [%ld>%ld]\n", + (LI)drop, (LI)dn->digits); + #endif + dn->lsu[0]=0; + dn->digits=1; + return dn; + } + msu=dn->lsu+D2U(dn->digits-drop)-1; // -> likely msu + cut=MSUDIGITS(dn->digits-drop); // digits to be in use in msu + if (cut!=DECDPUN) *msu%=powers[cut]; // clear left digits + // that may have left leading zero digits, so do a proper count... + dn->digits=decGetDigits(dn->lsu, msu-dn->lsu+1); + return dn; + } // decDecap + +/* ------------------------------------------------------------------ */ +/* decBiStr -- compare string with pairwise options */ +/* */ +/* targ is the string to compare */ +/* str1 is one of the strings to compare against (length may be 0) */ +/* str2 is the other; it must be the same length as str1 */ +/* */ +/* returns 1 if strings compare equal, (that is, it is the same */ +/* length as str1 and str2, and each character of targ is in either */ +/* str1 or str2 in the corresponding position), or 0 otherwise */ +/* */ +/* This is used for generic caseless compare, including the awkward */ +/* case of the Turkish dotted and dotless Is. Use as (for example): */ +/* if (decBiStr(test, "mike", "MIKE")) ... */ +/* ------------------------------------------------------------------ */ +static Flag decBiStr(const char *targ, const char *str1, const char *str2) { + for (;;targ++, str1++, str2++) { + if (*targ!=*str1 && *targ!=*str2) return 0; + // *targ has a match in one (or both, if terminator) + if (*targ=='\0') break; + } // forever + return 1; + } // decBiStr + +/* ------------------------------------------------------------------ */ +/* decNaNs -- handle NaN operand or operands */ +/* */ +/* res is the result number */ +/* lhs is the first operand */ +/* rhs is the second operand, or NULL if none */ +/* context is used to limit payload length */ +/* status contains the current status */ +/* returns res in case convenient */ +/* */ +/* Called when one or both operands is a NaN, and propagates the */ +/* appropriate result to res. When an sNaN is found, it is changed */ +/* to a qNaN and Invalid operation is set. */ +/* ------------------------------------------------------------------ */ +static decNumber * decNaNs(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set, + uInt *status) { + // This decision tree ends up with LHS being the source pointer, + // and status updated if need be + if (lhs->bits & DECSNAN) + *status|=DEC_Invalid_operation | DEC_sNaN; + else if (rhs==NULL); + else if (rhs->bits & DECSNAN) { + lhs=rhs; + *status|=DEC_Invalid_operation | DEC_sNaN; + } + else if (lhs->bits & DECNAN); + else lhs=rhs; + + // propagate the payload + if (lhs->digits<=set->digits) decNumberCopy(res, lhs); // easy + else { // too long + const Unit *ul; + Unit *ur, *uresp1; + // copy safe number of units, then decapitate + res->bits=lhs->bits; // need sign etc. + uresp1=res->lsu+D2U(set->digits); + for (ur=res->lsu, ul=lhs->lsu; urdigits=D2U(set->digits)*DECDPUN; + // maybe still too long + if (res->digits>set->digits) decDecap(res, res->digits-set->digits); + } + + res->bits&=~DECSNAN; // convert any sNaN to NaN, while + res->bits|=DECNAN; // .. preserving sign + res->exponent=0; // clean exponent + // [coefficient was copied/decapitated] + return res; + } // decNaNs + +/* ------------------------------------------------------------------ */ +/* decStatus -- apply non-zero status */ +/* */ +/* dn is the number to set if error */ +/* status contains the current status (not yet in context) */ +/* set is the context */ +/* */ +/* If the status is an error status, the number is set to a NaN, */ +/* unless the error was an overflow, divide-by-zero, or underflow, */ +/* in which case the number will have already been set. */ +/* */ +/* The context status is then updated with the new status. Note that */ +/* this may raise a signal, so control may never return from this */ +/* routine (hence resources must be recovered before it is called). */ +/* ------------------------------------------------------------------ */ +static void decStatus(decNumber *dn, uInt status, decContext *set) { + if (status & DEC_NaNs) { // error status -> NaN + // if cause was an sNaN, clear and propagate [NaN is already set up] + if (status & DEC_sNaN) status&=~DEC_sNaN; + else { + decNumberZero(dn); // other error: clean throughout + dn->bits=DECNAN; // and make a quiet NaN + } + } + decContextSetStatus(set, status); // [may not return] + return; + } // decStatus + +/* ------------------------------------------------------------------ */ +/* decGetDigits -- count digits in a Units array */ +/* */ +/* uar is the Unit array holding the number (this is often an */ +/* accumulator of some sort) */ +/* len is the length of the array in units [>=1] */ +/* */ +/* returns the number of (significant) digits in the array */ +/* */ +/* All leading zeros are excluded, except the last if the array has */ +/* only zero Units. */ +/* ------------------------------------------------------------------ */ +// This may be called twice during some operations. +static Int decGetDigits(Unit *uar, Int len) { + Unit *up=uar+(len-1); // -> msu + Int digits=(len-1)*DECDPUN+1; // possible digits excluding msu + #if DECDPUN>4 + uInt const *pow; // work + #endif + // (at least 1 in final msu) + #if DECCHECK + if (len<1) printf("decGetDigits called with len<1 [%ld]\n", (LI)len); + #endif + + for (; up>=uar; up--) { + if (*up==0) { // unit is all 0s + if (digits==1) break; // a zero has one digit + digits-=DECDPUN; // adjust for 0 unit + continue;} + // found the first (most significant) non-zero Unit + #if DECDPUN>1 // not done yet + if (*up<10) break; // is 1-9 + digits++; + #if DECDPUN>2 // not done yet + if (*up<100) break; // is 10-99 + digits++; + #if DECDPUN>3 // not done yet + if (*up<1000) break; // is 100-999 + digits++; + #if DECDPUN>4 // count the rest ... + for (pow=&powers[4]; *up>=*pow; pow++) digits++; + #endif + #endif + #endif + #endif + break; + } // up + return digits; + } // decGetDigits + +#if DECTRACE | DECCHECK +/* ------------------------------------------------------------------ */ +/* decNumberShow -- display a number [debug aid] */ +/* dn is the number to show */ +/* */ +/* Shows: sign, exponent, coefficient (msu first), digits */ +/* or: sign, special-value */ +/* ------------------------------------------------------------------ */ +// this is public so other modules can use it +void decNumberShow(const decNumber *dn) { + const Unit *up; // work + uInt u, d; // .. + Int cut; // .. + char isign='+'; // main sign + if (dn==NULL) { + printf("NULL\n"); + return;} + if (decNumberIsNegative(dn)) isign='-'; + printf(" >> %c ", isign); + if (dn->bits&DECSPECIAL) { // Is a special value + if (decNumberIsInfinite(dn)) printf("Infinity"); + else { // a NaN + if (dn->bits&DECSNAN) printf("sNaN"); // signalling NaN + else printf("NaN"); + } + // if coefficient and exponent are 0, no more to do + if (dn->exponent==0 && dn->digits==1 && *dn->lsu==0) { + printf("\n"); + return;} + // drop through to report other information + printf(" "); + } + + // now carefully display the coefficient + up=dn->lsu+D2U(dn->digits)-1; // msu + printf("%ld", (LI)*up); + for (up=up-1; up>=dn->lsu; up--) { + u=*up; + printf(":"); + for (cut=DECDPUN-1; cut>=0; cut--) { + d=u/powers[cut]; + u-=d*powers[cut]; + printf("%ld", (LI)d); + } // cut + } // up + if (dn->exponent!=0) { + char esign='+'; + if (dn->exponent<0) esign='-'; + printf(" E%c%ld", esign, (LI)abs(dn->exponent)); + } + printf(" [%ld]\n", (LI)dn->digits); + } // decNumberShow +#endif + +#if DECTRACE || DECCHECK +/* ------------------------------------------------------------------ */ +/* decDumpAr -- display a unit array [debug/check aid] */ +/* name is a single-character tag name */ +/* ar is the array to display */ +/* len is the length of the array in Units */ +/* ------------------------------------------------------------------ */ +static void decDumpAr(char name, const Unit *ar, Int len) { + Int i; + const char *spec; + #if DECDPUN==9 + spec="%09d "; + #elif DECDPUN==8 + spec="%08d "; + #elif DECDPUN==7 + spec="%07d "; + #elif DECDPUN==6 + spec="%06d "; + #elif DECDPUN==5 + spec="%05d "; + #elif DECDPUN==4 + spec="%04d "; + #elif DECDPUN==3 + spec="%03d "; + #elif DECDPUN==2 + spec="%02d "; + #else + spec="%d "; + #endif + printf(" :%c: ", name); + for (i=len-1; i>=0; i--) { + if (i==len-1) printf("%ld ", (LI)ar[i]); + else printf(spec, ar[i]); + } + printf("\n"); + return;} +#endif + +#if DECCHECK +/* ------------------------------------------------------------------ */ +/* decCheckOperands -- check operand(s) to a routine */ +/* res is the result structure (not checked; it will be set to */ +/* quiet NaN if error found (and it is not NULL)) */ +/* lhs is the first operand (may be DECUNRESU) */ +/* rhs is the second (may be DECUNUSED) */ +/* set is the context (may be DECUNCONT) */ +/* returns 0 if both operands, and the context are clean, or 1 */ +/* otherwise (in which case the context will show an error, */ +/* unless NULL). Note that res is not cleaned; caller should */ +/* handle this so res=NULL case is safe. */ +/* The caller is expected to abandon immediately if 1 is returned. */ +/* ------------------------------------------------------------------ */ +static Flag decCheckOperands(decNumber *res, const decNumber *lhs, + const decNumber *rhs, decContext *set) { + Flag bad=0; + if (set==NULL) { // oops; hopeless + #if DECTRACE || DECVERB + printf("Reference to context is NULL.\n"); + #endif + bad=1; + return 1;} + else if (set!=DECUNCONT + && (set->digits<1 || set->round>=DEC_ROUND_MAX)) { + bad=1; + #if DECTRACE || DECVERB + printf("Bad context [digits=%ld round=%ld].\n", + (LI)set->digits, (LI)set->round); + #endif + } + else { + if (res==NULL) { + bad=1; + #if DECTRACE + // this one not DECVERB as standard tests include NULL + printf("Reference to result is NULL.\n"); + #endif + } + if (!bad && lhs!=DECUNUSED) bad=(decCheckNumber(lhs)); + if (!bad && rhs!=DECUNUSED) bad=(decCheckNumber(rhs)); + } + if (bad) { + if (set!=DECUNCONT) decContextSetStatus(set, DEC_Invalid_operation); + if (res!=DECUNRESU && res!=NULL) { + decNumberZero(res); + res->bits=DECNAN; // qNaN + } + } + return bad; + } // decCheckOperands + +/* ------------------------------------------------------------------ */ +/* decCheckNumber -- check a number */ +/* dn is the number to check */ +/* returns 0 if the number is clean, or 1 otherwise */ +/* */ +/* The number is considered valid if it could be a result from some */ +/* operation in some valid context. */ +/* ------------------------------------------------------------------ */ +static Flag decCheckNumber(const decNumber *dn) { + const Unit *up; // work + uInt maxuint; // .. + Int ae, d, digits; // .. + Int emin, emax; // .. + + if (dn==NULL) { // hopeless + #if DECTRACE + // this one not DECVERB as standard tests include NULL + printf("Reference to decNumber is NULL.\n"); + #endif + return 1;} + + // check special values + if (dn->bits & DECSPECIAL) { + if (dn->exponent!=0) { + #if DECTRACE || DECVERB + printf("Exponent %ld (not 0) for a special value [%02x].\n", + (LI)dn->exponent, dn->bits); + #endif + return 1;} + + // 2003.09.08: NaNs may now have coefficients, so next tests Inf only + if (decNumberIsInfinite(dn)) { + if (dn->digits!=1) { + #if DECTRACE || DECVERB + printf("Digits %ld (not 1) for an infinity.\n", (LI)dn->digits); + #endif + return 1;} + if (*dn->lsu!=0) { + #if DECTRACE || DECVERB + printf("LSU %ld (not 0) for an infinity.\n", (LI)*dn->lsu); + #endif + decDumpAr('I', dn->lsu, D2U(dn->digits)); + return 1;} + } // Inf + // 2002.12.26: negative NaNs can now appear through proposed IEEE + // concrete formats (decimal64, etc.). + return 0; + } + + // check the coefficient + if (dn->digits<1 || dn->digits>DECNUMMAXP) { + #if DECTRACE || DECVERB + printf("Digits %ld in number.\n", (LI)dn->digits); + #endif + return 1;} + + d=dn->digits; + + for (up=dn->lsu; d>0; up++) { + if (d>DECDPUN) maxuint=DECDPUNMAX; + else { // reached the msu + maxuint=powers[d]-1; + if (dn->digits>1 && *upmaxuint) { + #if DECTRACE || DECVERB + printf("Bad Unit [%08lx] in %ld-digit number at offset %ld [maxuint %ld].\n", + (LI)*up, (LI)dn->digits, (LI)(up-dn->lsu), (LI)maxuint); + #endif + return 1;} + d-=DECDPUN; + } + + // check the exponent. Note that input operands can have exponents + // which are out of the set->emin/set->emax and set->digits range + // (just as they can have more digits than set->digits). + ae=dn->exponent+dn->digits-1; // adjusted exponent + emax=DECNUMMAXE; + emin=DECNUMMINE; + digits=DECNUMMAXP; + if (ae+emax) { + #if DECTRACE || DECVERB + printf("Adjusted exponent overflow [%ld].\n", (LI)ae); + decNumberShow(dn); + #endif + return 1;} + + return 0; // it's OK + } // decCheckNumber + +/* ------------------------------------------------------------------ */ +/* decCheckInexact -- check a normal finite inexact result has digits */ +/* dn is the number to check */ +/* set is the context (for status and precision) */ +/* sets Invalid operation, etc., if some digits are missing */ +/* [this check is not made for DECSUBSET compilation or when */ +/* subnormal is not set] */ +/* ------------------------------------------------------------------ */ +static void decCheckInexact(const decNumber *dn, decContext *set) { + #if !DECSUBSET && DECEXTFLAG + if ((set->status & (DEC_Inexact|DEC_Subnormal))==DEC_Inexact + && (set->digits!=dn->digits) && !(dn->bits & DECSPECIAL)) { + #if DECTRACE || DECVERB + printf("Insufficient digits [%ld] on normal Inexact result.\n", + (LI)dn->digits); + decNumberShow(dn); + #endif + decContextSetStatus(set, DEC_Invalid_operation); + } + #else + // next is a noop for quiet compiler + if (dn!=NULL && dn->digits==0) set->status|=DEC_Invalid_operation; + #endif + return; + } // decCheckInexact +#endif + +#if DECALLOC +#undef malloc +#undef free +/* ------------------------------------------------------------------ */ +/* decMalloc -- accountable allocation routine */ +/* n is the number of bytes to allocate */ +/* */ +/* Semantics is the same as the stdlib malloc routine, but bytes */ +/* allocated are accounted for globally, and corruption fences are */ +/* added before and after the 'actual' storage. */ +/* ------------------------------------------------------------------ */ +/* This routine allocates storage with an extra twelve bytes; 8 are */ +/* at the start and hold: */ +/* 0-3 the original length requested */ +/* 4-7 buffer corruption detection fence (DECFENCE, x4) */ +/* The 4 bytes at the end also hold a corruption fence (DECFENCE, x4) */ +/* ------------------------------------------------------------------ */ +static void *decMalloc(size_t n) { + uInt size=n+12; // true size + void *alloc; // -> allocated storage + uByte *b, *b0; // work + uInt uiwork; // for macros + + alloc=malloc(size); // -> allocated storage + if (alloc==NULL) return NULL; // out of strorage + b0=(uByte *)alloc; // as bytes + decAllocBytes+=n; // account for storage + UBFROMUI(alloc, n); // save n + // printf(" alloc ++ dAB: %ld (%ld)\n", (LI)decAllocBytes, (LI)n); + for (b=b0+4; b play area + } // decMalloc + +/* ------------------------------------------------------------------ */ +/* decFree -- accountable free routine */ +/* alloc is the storage to free */ +/* */ +/* Semantics is the same as the stdlib malloc routine, except that */ +/* the global storage accounting is updated and the fences are */ +/* checked to ensure that no routine has written 'out of bounds'. */ +/* ------------------------------------------------------------------ */ +/* This routine first checks that the fences have not been corrupted. */ +/* It then frees the storage using the 'truw' storage address (that */ +/* is, offset by 8). */ +/* ------------------------------------------------------------------ */ +static void decFree(void *alloc) { + uInt n; // original length + uByte *b, *b0; // work + uInt uiwork; // for macros + + if (alloc==NULL) return; // allowed; it's a nop + b0=(uByte *)alloc; // as bytes + b0-=8; // -> true start of storage + n=UBTOUI(b0); // lift length + for (b=b0+4; b0 */ + /* and <10; 3 or powers of 2 are best]. */ + + /* DECNUMDIGITS is the default number of digits that can be held in */ + /* the structure. If undefined, 1 is assumed and it is assumed */ + /* that the structure will be immediately followed by extra space, */ + /* as required. DECNUMDIGITS is always >0. */ + #if !defined(DECNUMDIGITS) + #define DECNUMDIGITS 1 + #endif + + /* The size (integer data type) of each unit is determined by the */ + /* number of digits it will hold. */ + #if DECDPUN<=2 + #define decNumberUnit uint8_t + #elif DECDPUN<=4 + #define decNumberUnit uint16_t + #else + #define decNumberUnit uint32_t + #endif + /* The number of units needed is ceil(DECNUMDIGITS/DECDPUN) */ + #define DECNUMUNITS ((DECNUMDIGITS+DECDPUN-1)/DECDPUN) + + /* The data structure... */ + typedef struct { + int32_t digits; /* Count of digits in the coefficient; >0 */ + int32_t exponent; /* Unadjusted exponent, unbiased, in */ + /* range: -1999999997 through 999999999 */ + uint8_t bits; /* Indicator bits (see above) */ + /* Coefficient, from least significant unit */ + decNumberUnit lsu[DECNUMUNITS]; + } decNumber; + + /* Notes: */ + /* 1. If digits is > DECDPUN then there will one or more */ + /* decNumberUnits immediately following the first element of lsu.*/ + /* These contain the remaining (more significant) digits of the */ + /* number, and may be in the lsu array, or may be guaranteed by */ + /* some other mechanism (such as being contained in another */ + /* structure, or being overlaid on dynamically allocated */ + /* storage). */ + /* */ + /* Each integer of the coefficient (except potentially the last) */ + /* contains DECDPUN digits (e.g., a value in the range 0 through */ + /* 99999999 if DECDPUN is 8, or 0 through 999 if DECDPUN is 3). */ + /* */ + /* 2. A decNumber converted to a string may need up to digits+14 */ + /* characters. The worst cases (non-exponential and exponential */ + /* formats) are -0.00000{9...}# and -9.{9...}E+999999999# */ + /* (where # is '\0') */ + + + /* ---------------------------------------------------------------- */ + /* decNumber public functions and macros */ + /* ---------------------------------------------------------------- */ + /* Conversions */ + decNumber * decNumberFromInt32(decNumber *, int32_t); + decNumber * decNumberFromUInt32(decNumber *, uint32_t); + decNumber * decNumberFromString(decNumber *, const char *, decContext *); + char * decNumberToString(const decNumber *, char *); + char * decNumberToEngString(const decNumber *, char *); + uint32_t decNumberToUInt32(const decNumber *, decContext *); + int32_t decNumberToInt32(const decNumber *, decContext *); + uint8_t * decNumberGetBCD(const decNumber *, uint8_t *); + decNumber * decNumberSetBCD(decNumber *, const uint8_t *, uint32_t); + + /* Operators and elementary functions */ + decNumber * decNumberAbs(decNumber *, const decNumber *, decContext *); + decNumber * decNumberAdd(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberAnd(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberCompare(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberCompareSignal(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberCompareTotal(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberCompareTotalMag(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberDivide(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberDivideInteger(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberExp(decNumber *, const decNumber *, decContext *); + decNumber * decNumberFMA(decNumber *, const decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberInvert(decNumber *, const decNumber *, decContext *); + decNumber * decNumberLn(decNumber *, const decNumber *, decContext *); + decNumber * decNumberLogB(decNumber *, const decNumber *, decContext *); + decNumber * decNumberLog10(decNumber *, const decNumber *, decContext *); + decNumber * decNumberMax(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberMaxMag(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberMin(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberMinMag(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberMinus(decNumber *, const decNumber *, decContext *); + decNumber * decNumberMultiply(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberNormalize(decNumber *, const decNumber *, decContext *); + decNumber * decNumberOr(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberPlus(decNumber *, const decNumber *, decContext *); + decNumber * decNumberPower(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberQuantize(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberReduce(decNumber *, const decNumber *, decContext *); + decNumber * decNumberRemainder(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberRemainderNear(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberRescale(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberRotate(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberSameQuantum(decNumber *, const decNumber *, const decNumber *); + decNumber * decNumberScaleB(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberShift(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberSquareRoot(decNumber *, const decNumber *, decContext *); + decNumber * decNumberSubtract(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberToIntegralExact(decNumber *, const decNumber *, decContext *); + decNumber * decNumberToIntegralValue(decNumber *, const decNumber *, decContext *); + decNumber * decNumberXor(decNumber *, const decNumber *, const decNumber *, decContext *); + + /* Utilities */ + enum decClass decNumberClass(const decNumber *, decContext *); + const char * decNumberClassToString(enum decClass); + decNumber * decNumberCopy(decNumber *, const decNumber *); + decNumber * decNumberCopyAbs(decNumber *, const decNumber *); + decNumber * decNumberCopyNegate(decNumber *, const decNumber *); + decNumber * decNumberCopySign(decNumber *, const decNumber *, const decNumber *); + decNumber * decNumberNextMinus(decNumber *, const decNumber *, decContext *); + decNumber * decNumberNextPlus(decNumber *, const decNumber *, decContext *); + decNumber * decNumberNextToward(decNumber *, const decNumber *, const decNumber *, decContext *); + decNumber * decNumberTrim(decNumber *); + const char * decNumberVersion(void); + decNumber * decNumberZero(decNumber *); + + /* Functions for testing decNumbers (normality depends on context) */ + int32_t decNumberIsNormal(const decNumber *, decContext *); + int32_t decNumberIsSubnormal(const decNumber *, decContext *); + + /* Macros for testing decNumber *dn */ + #define decNumberIsCanonical(dn) (1) /* All decNumbers are saintly */ + #define decNumberIsFinite(dn) (((dn)->bits&DECSPECIAL)==0) + #define decNumberIsInfinite(dn) (((dn)->bits&DECINF)!=0) + #define decNumberIsNaN(dn) (((dn)->bits&(DECNAN|DECSNAN))!=0) + #define decNumberIsNegative(dn) (((dn)->bits&DECNEG)!=0) + #define decNumberIsQNaN(dn) (((dn)->bits&(DECNAN))!=0) + #define decNumberIsSNaN(dn) (((dn)->bits&(DECSNAN))!=0) + #define decNumberIsSpecial(dn) (((dn)->bits&DECSPECIAL)!=0) + #define decNumberIsZero(dn) (*(dn)->lsu==0 \ + && (dn)->digits==1 \ + && (((dn)->bits&DECSPECIAL)==0)) + #define decNumberRadix(dn) (10) + +#endif diff --git a/extern/decNumber/decNumberLocal.h b/extern/decNumber/decNumberLocal.h new file mode 100644 index 0000000000..cfd3e74e23 --- /dev/null +++ b/extern/decNumber/decNumberLocal.h @@ -0,0 +1,757 @@ +/* ------------------------------------------------------------------ */ +/* decNumber package local type, tuning, and macro definitions */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This header file is included by all modules in the decNumber */ +/* library, and contains local type definitions, tuning parameters, */ +/* etc. It should not need to be used by application programs. */ +/* decNumber.h or one of decDouble (etc.) must be included first. */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECNUMBERLOC) + #define DECNUMBERLOC + #define DECVERSION "decNumber 3.68" /* Package Version [16 max.] */ + #define DECNLAUTHOR "Mike Cowlishaw" /* Who to blame */ + + #include /* for abs */ + #include /* for memset, strcpy */ + + /* Conditional code flag -- set this to match hardware platform */ + #if !defined(DECLITEND) + #define DECLITEND 1 /* 1=little-endian, 0=big-endian */ + #endif + + /* Conditional code flag -- set this to 1 for best performance */ + #if !defined(DECUSE64) + #define DECUSE64 1 /* 1=use int64s, 0=int32 & smaller only */ + #endif + + /* Conditional code flag -- set this to 0 to exclude printf calls */ + #if !defined(DECPRINT) + #define DECPRINT 1 /* 1=allow printf calls; 0=no printf */ + #endif + + /* Conditional check flags -- set these to 0 for best performance */ + #if !defined(DECCHECK) + #define DECCHECK 0 /* 1 to enable robust checking */ + #endif + #if !defined(DECALLOC) + #define DECALLOC 0 /* 1 to enable memory accounting */ + #endif + #if !defined(DECTRACE) + #define DECTRACE 0 /* 1 to trace certain internals, etc. */ + #endif + + /* Tuning parameter for decNumber (arbitrary precision) module */ + #if !defined(DECBUFFER) + #define DECBUFFER 36 /* Size basis for local buffers. This */ + /* should be a common maximum precision */ + /* rounded up to a multiple of 4; must */ + /* be zero or positive. */ + #endif + + + /* ---------------------------------------------------------------- */ + /* Check parameter dependencies */ + /* ---------------------------------------------------------------- */ + #if DECCHECK & !DECPRINT + #error DECCHECK needs DECPRINT to be useful + #endif + #if DECALLOC & !DECPRINT + #error DECALLOC needs DECPRINT to be useful + #endif + #if DECTRACE & !DECPRINT + #error DECTRACE needs DECPRINT to be useful + #endif + + /* ---------------------------------------------------------------- */ + /* Definitions for all modules (general-purpose) */ + /* ---------------------------------------------------------------- */ + + /* Local names for common types -- for safety, decNumber modules do */ + /* not use int or long directly. */ + #define Flag uint8_t + #define Byte int8_t + #define uByte uint8_t + #define Short int16_t + #define uShort uint16_t + #define Int int32_t + #define uInt uint32_t + #define Unit decNumberUnit + #if DECUSE64 + #define Long int64_t + #define uLong uint64_t + #endif + + /* Development-use definitions */ + typedef long int LI; /* for printf arguments only */ + #define DECNOINT 0 /* 1 to check no internal use of 'int' */ + /* or stdint types */ + #if DECNOINT + /* if these interfere with your C includes, do not set DECNOINT */ + #define int ? /* enable to ensure that plain C 'int' */ + #define long ?? /* .. or 'long' types are not used */ + #endif + + /* Shared lookup tables */ + extern const uByte DECSTICKYTAB[10]; /* re-round digits if sticky */ + extern const uInt DECPOWERS[10]; /* powers of ten table */ + /* The following are included from decDPD.h */ + extern const uShort DPD2BIN[1024]; /* DPD -> 0-999 */ + extern const uShort BIN2DPD[1000]; /* 0-999 -> DPD */ + extern const uInt DPD2BINK[1024]; /* DPD -> 0-999000 */ + extern const uInt DPD2BINM[1024]; /* DPD -> 0-999000000 */ + extern const uByte DPD2BCD8[4096]; /* DPD -> ddd + len */ + extern const uByte BIN2BCD8[4000]; /* 0-999 -> ddd + len */ + extern const uShort BCD2DPD[2458]; /* 0-0x999 -> DPD (0x999=2457)*/ + + /* LONGMUL32HI -- set w=(u*v)>>32, where w, u, and v are uInts */ + /* (that is, sets w to be the high-order word of the 64-bit result; */ + /* the low-order word is simply u*v.) */ + /* This version is derived from Knuth via Hacker's Delight; */ + /* it seems to optimize better than some others tried */ + #define LONGMUL32HI(w, u, v) { \ + uInt u0, u1, v0, v1, w0, w1, w2, t; \ + u0=u & 0xffff; u1=u>>16; \ + v0=v & 0xffff; v1=v>>16; \ + w0=u0*v0; \ + t=u1*v0 + (w0>>16); \ + w1=t & 0xffff; w2=t>>16; \ + w1=u0*v1 + w1; \ + (w)=u1*v1 + w2 + (w1>>16);} + + /* ROUNDUP -- round an integer up to a multiple of n */ + #define ROUNDUP(i, n) ((((i)+(n)-1)/n)*n) + #define ROUNDUP4(i) (((i)+3)&~3) /* special for n=4 */ + + /* ROUNDDOWN -- round an integer down to a multiple of n */ + #define ROUNDDOWN(i, n) (((i)/n)*n) + #define ROUNDDOWN4(i) ((i)&~3) /* special for n=4 */ + + /* References to multi-byte sequences under different sizes; these */ + /* require locally declared variables, but do not violate strict */ + /* aliasing or alignment (as did the UINTAT simple cast to uInt). */ + /* Variables needed are uswork, uiwork, etc. [so do not use at same */ + /* level in an expression, e.g., UBTOUI(x)==UBTOUI(y) may fail]. */ + + /* Return a uInt, etc., from bytes starting at a char* or uByte* */ + #define UBTOUS(b) (memcpy((void *)&uswork, b, 2), uswork) + #define UBTOUI(b) (memcpy((void *)&uiwork, b, 4), uiwork) + + /* Store a uInt, etc., into bytes starting at a char* or uByte*. */ + /* Returns i, evaluated, for convenience; has to use uiwork because */ + /* i may be an expression. */ + #define UBFROMUS(b, i) (uswork=(i), memcpy(b, (void *)&uswork, 2), uswork) + #define UBFROMUI(b, i) (uiwork=(i), memcpy(b, (void *)&uiwork, 4), uiwork) + + /* X10 and X100 -- multiply integer i by 10 or 100 */ + /* [shifts are usually faster than multiply; could be conditional] */ + #define X10(i) (((i)<<1)+((i)<<3)) + #define X100(i) (((i)<<2)+((i)<<5)+((i)<<6)) + + /* MAXI and MINI -- general max & min (not in ANSI) for integers */ + #define MAXI(x,y) ((x)<(y)?(y):(x)) + #define MINI(x,y) ((x)>(y)?(y):(x)) + + /* Useful constants */ + #define BILLION 1000000000 /* 10**9 */ + /* CHARMASK: 0x30303030 for ASCII/UTF8; 0xF0F0F0F0 for EBCDIC */ + #define CHARMASK ((((((((uInt)'0')<<8)+'0')<<8)+'0')<<8)+'0') + + + /* ---------------------------------------------------------------- */ + /* Definitions for arbitary-precision modules (only valid after */ + /* decNumber.h has been included) */ + /* ---------------------------------------------------------------- */ + + /* Limits and constants */ + #define DECNUMMAXP 999999999 /* maximum precision code can handle */ + #define DECNUMMAXE 999999999 /* maximum adjusted exponent ditto */ + #define DECNUMMINE -999999999 /* minimum adjusted exponent ditto */ + #if (DECNUMMAXP != DEC_MAX_DIGITS) + #error Maximum digits mismatch + #endif + #if (DECNUMMAXE != DEC_MAX_EMAX) + #error Maximum exponent mismatch + #endif + #if (DECNUMMINE != DEC_MIN_EMIN) + #error Minimum exponent mismatch + #endif + + /* Set DECDPUNMAX -- the maximum integer that fits in DECDPUN */ + /* digits, and D2UTABLE -- the initializer for the D2U table */ + #if DECDPUN==1 + #define DECDPUNMAX 9 + #define D2UTABLE {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17, \ + 18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, \ + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47, \ + 48,49} + #elif DECDPUN==2 + #define DECDPUNMAX 99 + #define D2UTABLE {0,1,1,2,2,3,3,4,4,5,5,6,6,7,7,8,8,9,9,10,10, \ + 11,11,12,12,13,13,14,14,15,15,16,16,17,17,18, \ + 18,19,19,20,20,21,21,22,22,23,23,24,24,25} + #elif DECDPUN==3 + #define DECDPUNMAX 999 + #define D2UTABLE {0,1,1,1,2,2,2,3,3,3,4,4,4,5,5,5,6,6,6,7,7,7, \ + 8,8,8,9,9,9,10,10,10,11,11,11,12,12,12,13,13, \ + 13,14,14,14,15,15,15,16,16,16,17} + #elif DECDPUN==4 + #define DECDPUNMAX 9999 + #define D2UTABLE {0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6, \ + 6,6,6,7,7,7,7,8,8,8,8,9,9,9,9,10,10,10,10,11, \ + 11,11,11,12,12,12,12,13} + #elif DECDPUN==5 + #define DECDPUNMAX 99999 + #define D2UTABLE {0,1,1,1,1,1,2,2,2,2,2,3,3,3,3,3,4,4,4,4,4,5, \ + 5,5,5,5,6,6,6,6,6,7,7,7,7,7,8,8,8,8,8,9,9,9, \ + 9,9,10,10,10,10} + #elif DECDPUN==6 + #define DECDPUNMAX 999999 + #define D2UTABLE {0,1,1,1,1,1,1,2,2,2,2,2,2,3,3,3,3,3,3,4,4,4, \ + 4,4,4,5,5,5,5,5,5,6,6,6,6,6,6,7,7,7,7,7,7,8, \ + 8,8,8,8,8,9} + #elif DECDPUN==7 + #define DECDPUNMAX 9999999 + #define D2UTABLE {0,1,1,1,1,1,1,1,2,2,2,2,2,2,2,3,3,3,3,3,3,3, \ + 4,4,4,4,4,4,4,5,5,5,5,5,5,5,6,6,6,6,6,6,6,7, \ + 7,7,7,7,7,7} + #elif DECDPUN==8 + #define DECDPUNMAX 99999999 + #define D2UTABLE {0,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,3,3,3,3,3, \ + 3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,6,6,6, \ + 6,6,6,6,6,7} + #elif DECDPUN==9 + #define DECDPUNMAX 999999999 + #define D2UTABLE {0,1,1,1,1,1,1,1,1,1,2,2,2,2,2,2,2,2,2,3,3,3, \ + 3,3,3,3,3,3,4,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5, \ + 5,5,6,6,6,6} + #elif defined(DECDPUN) + #error DECDPUN must be in the range 1-9 + #endif + + /* ----- Shared data (in decNumber.c) ----- */ + /* Public lookup table used by the D2U macro (see below) */ + #define DECMAXD2U 49 + extern const uByte d2utable[DECMAXD2U+1]; + + /* ----- Macros ----- */ + /* ISZERO -- return true if decNumber dn is a zero */ + /* [performance-critical in some situations] */ + #define ISZERO(dn) decNumberIsZero(dn) /* now just a local name */ + + /* D2U -- return the number of Units needed to hold d digits */ + /* (runtime version, with table lookaside for small d) */ + #if DECDPUN==8 + #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+7)>>3)) + #elif DECDPUN==4 + #define D2U(d) ((unsigned)((d)<=DECMAXD2U?d2utable[d]:((d)+3)>>2)) + #else + #define D2U(d) ((d)<=DECMAXD2U?d2utable[d]:((d)+DECDPUN-1)/DECDPUN) + #endif + /* SD2U -- static D2U macro (for compile-time calculation) */ + #define SD2U(d) (((d)+DECDPUN-1)/DECDPUN) + + /* MSUDIGITS -- returns digits in msu, from digits, calculated */ + /* using D2U */ + #define MSUDIGITS(d) ((d)-(D2U(d)-1)*DECDPUN) + + /* D2N -- return the number of decNumber structs that would be */ + /* needed to contain that number of digits (and the initial */ + /* decNumber struct) safely. Note that one Unit is included in the */ + /* initial structure. Used for allocating space that is aligned on */ + /* a decNumber struct boundary. */ + #define D2N(d) \ + ((((SD2U(d)-1)*sizeof(Unit))+sizeof(decNumber)*2-1)/sizeof(decNumber)) + + /* TODIGIT -- macro to remove the leading digit from the unsigned */ + /* integer u at column cut (counting from the right, LSD=0) and */ + /* place it as an ASCII character into the character pointed to by */ + /* c. Note that cut must be <= 9, and the maximum value for u is */ + /* 2,000,000,000 (as is needed for negative exponents of */ + /* subnormals). The unsigned integer pow is used as a temporary */ + /* variable. */ + #define TODIGIT(u, cut, c, pow) { \ + *(c)='0'; \ + pow=DECPOWERS[cut]*2; \ + if ((u)>pow) { \ + pow*=4; \ + if ((u)>=pow) {(u)-=pow; *(c)+=8;} \ + pow/=2; \ + if ((u)>=pow) {(u)-=pow; *(c)+=4;} \ + pow/=2; \ + } \ + if ((u)>=pow) {(u)-=pow; *(c)+=2;} \ + pow/=2; \ + if ((u)>=pow) {(u)-=pow; *(c)+=1;} \ + } + + /* ---------------------------------------------------------------- */ + /* Definitions for fixed-precision modules (only valid after */ + /* decSingle.h, decDouble.h, or decQuad.h has been included) */ + /* ---------------------------------------------------------------- */ + + /* bcdnum -- a structure describing a format-independent finite */ + /* number, whose coefficient is a string of bcd8 uBytes */ + typedef struct { + uByte *msd; /* -> most significant digit */ + uByte *lsd; /* -> least ditto */ + uInt sign; /* 0=positive, DECFLOAT_Sign=negative */ + Int exponent; /* Unadjusted signed exponent (q), or */ + /* DECFLOAT_NaN etc. for a special */ + } bcdnum; + + /* Test if exponent or bcdnum exponent must be a special, etc. */ + #define EXPISSPECIAL(exp) ((exp)>=DECFLOAT_MinSp) + #define EXPISINF(exp) (exp==DECFLOAT_Inf) + #define EXPISNAN(exp) (exp==DECFLOAT_qNaN || exp==DECFLOAT_sNaN) + #define NUMISSPECIAL(num) (EXPISSPECIAL((num)->exponent)) + + /* Refer to a 32-bit word or byte in a decFloat (df) by big-endian */ + /* (array) notation (the 0 word or byte contains the sign bit), */ + /* automatically adjusting for endianness; similarly address a word */ + /* in the next-wider format (decFloatWider, or dfw) */ + #define DECWORDS (DECBYTES/4) + #define DECWWORDS (DECWBYTES/4) + #if DECLITEND + #define DFBYTE(df, off) ((df)->bytes[DECBYTES-1-(off)]) + #define DFWORD(df, off) ((df)->words[DECWORDS-1-(off)]) + #define DFWWORD(dfw, off) ((dfw)->words[DECWWORDS-1-(off)]) + #else + #define DFBYTE(df, off) ((df)->bytes[off]) + #define DFWORD(df, off) ((df)->words[off]) + #define DFWWORD(dfw, off) ((dfw)->words[off]) + #endif + + /* Tests for sign or specials, directly on DECFLOATs */ + #define DFISSIGNED(df) ((DFWORD(df, 0)&0x80000000)!=0) + #define DFISSPECIAL(df) ((DFWORD(df, 0)&0x78000000)==0x78000000) + #define DFISINF(df) ((DFWORD(df, 0)&0x7c000000)==0x78000000) + #define DFISNAN(df) ((DFWORD(df, 0)&0x7c000000)==0x7c000000) + #define DFISQNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7c000000) + #define DFISSNAN(df) ((DFWORD(df, 0)&0x7e000000)==0x7e000000) + + /* Shared lookup tables */ + extern const uInt DECCOMBMSD[64]; /* Combination field -> MSD */ + extern const uInt DECCOMBFROM[48]; /* exp+msd -> Combination */ + + /* Private generic (utility) routine */ + #if DECCHECK || DECTRACE + extern void decShowNum(const bcdnum *, const char *); + #endif + + /* Format-dependent macros and constants */ + #if defined(DECPMAX) + + /* Useful constants */ + #define DECPMAX9 (ROUNDUP(DECPMAX, 9)/9) /* 'Pmax' in 10**9s */ + /* Top words for a zero */ + #define SINGLEZERO 0x22500000 + #define DOUBLEZERO 0x22380000 + #define QUADZERO 0x22080000 + /* [ZEROWORD is defined to be one of these in the DFISZERO macro] */ + + /* Format-dependent common tests: */ + /* DFISZERO -- test for (any) zero */ + /* DFISCCZERO -- test for coefficient continuation being zero */ + /* DFISCC01 -- test for coefficient contains only 0s and 1s */ + /* DFISINT -- test for finite and exponent q=0 */ + /* DFISUINT01 -- test for sign=0, finite, exponent q=0, and */ + /* MSD=0 or 1 */ + /* ZEROWORD is also defined here. */ + /* */ + /* In DFISZERO the first test checks the least-significant word */ + /* (most likely to be non-zero); the penultimate tests MSD and */ + /* DPDs in the signword, and the final test excludes specials and */ + /* MSD>7. DFISINT similarly has to allow for the two forms of */ + /* MSD codes. DFISUINT01 only has to allow for one form of MSD */ + /* code. */ + #if DECPMAX==7 + #define ZEROWORD SINGLEZERO + /* [test macros not needed except for Zero] */ + #define DFISZERO(df) ((DFWORD(df, 0)&0x1c0fffff)==0 \ + && (DFWORD(df, 0)&0x60000000)!=0x60000000) + #elif DECPMAX==16 + #define ZEROWORD DOUBLEZERO + #define DFISZERO(df) ((DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x1c03ffff)==0 \ + && (DFWORD(df, 0)&0x60000000)!=0x60000000)) + #define DFISINT(df) ((DFWORD(df, 0)&0x63fc0000)==0x22380000 \ + ||(DFWORD(df, 0)&0x7bfc0000)==0x6a380000) + #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbfc0000)==0x22380000) + #define DFISCCZERO(df) (DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x0003ffff)==0) + #define DFISCC01(df) ((DFWORD(df, 0)&~0xfffc9124)==0 \ + && (DFWORD(df, 1)&~0x49124491)==0) + #elif DECPMAX==34 + #define ZEROWORD QUADZERO + #define DFISZERO(df) ((DFWORD(df, 3)==0 \ + && DFWORD(df, 2)==0 \ + && DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x1c003fff)==0 \ + && (DFWORD(df, 0)&0x60000000)!=0x60000000)) + #define DFISINT(df) ((DFWORD(df, 0)&0x63ffc000)==0x22080000 \ + ||(DFWORD(df, 0)&0x7bffc000)==0x6a080000) + #define DFISUINT01(df) ((DFWORD(df, 0)&0xfbffc000)==0x22080000) + #define DFISCCZERO(df) (DFWORD(df, 3)==0 \ + && DFWORD(df, 2)==0 \ + && DFWORD(df, 1)==0 \ + && (DFWORD(df, 0)&0x00003fff)==0) + + #define DFISCC01(df) ((DFWORD(df, 0)&~0xffffc912)==0 \ + && (DFWORD(df, 1)&~0x44912449)==0 \ + && (DFWORD(df, 2)&~0x12449124)==0 \ + && (DFWORD(df, 3)&~0x49124491)==0) + #endif + + /* Macros to test if a certain 10 bits of a uInt or pair of uInts */ + /* are a canonical declet [higher or lower bits are ignored]. */ + /* declet is at offset 0 (from the right) in a uInt: */ + #define CANONDPD(dpd) (((dpd)&0x300)==0 || ((dpd)&0x6e)!=0x6e) + /* declet is at offset k (a multiple of 2) in a uInt: */ + #define CANONDPDOFF(dpd, k) (((dpd)&(0x300<<(k)))==0 \ + || ((dpd)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k))) + /* declet is at offset k (a multiple of 2) in a pair of uInts: */ + /* [the top 2 bits will always be in the more-significant uInt] */ + #define CANONDPDTWO(hi, lo, k) (((hi)&(0x300>>(32-(k))))==0 \ + || ((hi)&(0x6e>>(32-(k))))!=(0x6e>>(32-(k))) \ + || ((lo)&(((uInt)0x6e)<<(k)))!=(((uInt)0x6e)<<(k))) + + /* Macro to test whether a full-length (length DECPMAX) BCD8 */ + /* coefficient, starting at uByte u, is all zeros */ + /* Test just the LSWord first, then the remainder as a sequence */ + /* of tests in order to avoid same-level use of UBTOUI */ + #if DECPMAX==7 + #define ISCOEFFZERO(u) ( \ + UBTOUI((u)+DECPMAX-4)==0 \ + && UBTOUS((u)+DECPMAX-6)==0 \ + && *(u)==0) + #elif DECPMAX==16 + #define ISCOEFFZERO(u) ( \ + UBTOUI((u)+DECPMAX-4)==0 \ + && UBTOUI((u)+DECPMAX-8)==0 \ + && UBTOUI((u)+DECPMAX-12)==0 \ + && UBTOUI(u)==0) + #elif DECPMAX==34 + #define ISCOEFFZERO(u) ( \ + UBTOUI((u)+DECPMAX-4)==0 \ + && UBTOUI((u)+DECPMAX-8)==0 \ + && UBTOUI((u)+DECPMAX-12)==0 \ + && UBTOUI((u)+DECPMAX-16)==0 \ + && UBTOUI((u)+DECPMAX-20)==0 \ + && UBTOUI((u)+DECPMAX-24)==0 \ + && UBTOUI((u)+DECPMAX-28)==0 \ + && UBTOUI((u)+DECPMAX-32)==0 \ + && UBTOUS(u)==0) + #endif + + /* Macros and masks for the sign, exponent continuation, and MSD */ + /* Get the sign as DECFLOAT_Sign or 0 */ + #define GETSIGN(df) (DFWORD(df, 0)&0x80000000) + /* Get the exponent continuation from a decFloat *df as an Int */ + #define GETECON(df) ((Int)((DFWORD((df), 0)&0x03ffffff)>>(32-6-DECECONL))) + /* Ditto, from the next-wider format */ + #define GETWECON(df) ((Int)((DFWWORD((df), 0)&0x03ffffff)>>(32-6-DECWECONL))) + /* Get the biased exponent similarly */ + #define GETEXP(df) ((Int)(DECCOMBEXP[DFWORD((df), 0)>>26]+GETECON(df))) + /* Get the unbiased exponent similarly */ + #define GETEXPUN(df) ((Int)GETEXP(df)-DECBIAS) + /* Get the MSD similarly (as uInt) */ + #define GETMSD(df) (DECCOMBMSD[DFWORD((df), 0)>>26]) + + /* Compile-time computes of the exponent continuation field masks */ + /* full exponent continuation field: */ + #define ECONMASK ((0x03ffffff>>(32-6-DECECONL))<<(32-6-DECECONL)) + /* same, not including its first digit (the qNaN/sNaN selector): */ + #define ECONNANMASK ((0x01ffffff>>(32-6-DECECONL))<<(32-6-DECECONL)) + + /* Macros to decode the coefficient in a finite decFloat *df into */ + /* a BCD string (uByte *bcdin) of length DECPMAX uBytes. */ + + /* In-line sequence to convert least significant 10 bits of uInt */ + /* dpd to three BCD8 digits starting at uByte u. Note that an */ + /* extra byte is written to the right of the three digits because */ + /* four bytes are moved at a time for speed; the alternative */ + /* macro moves exactly three bytes (usually slower). */ + #define dpd2bcd8(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 4) + #define dpd2bcd83(u, dpd) memcpy(u, &DPD2BCD8[((dpd)&0x3ff)*4], 3) + + /* Decode the declets. After extracting each one, it is decoded */ + /* to BCD8 using a table lookup (also used for variable-length */ + /* decode). Each DPD decode is 3 bytes BCD8 plus a one-byte */ + /* length which is not used, here). Fixed-length 4-byte moves */ + /* are fast, however, almost everywhere, and so are used except */ + /* for the final three bytes (to avoid overrun). The code below */ + /* is 36 instructions for Doubles and about 70 for Quads, even */ + /* on IA32. */ + + /* Two macros are defined for each format: */ + /* GETCOEFF extracts the coefficient of the current format */ + /* GETWCOEFF extracts the coefficient of the next-wider format. */ + /* The latter is a copy of the next-wider GETCOEFF using DFWWORD. */ + + #if DECPMAX==7 + #define GETCOEFF(df, bcd) { \ + uInt sourhi=DFWORD(df, 0); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>10); \ + dpd2bcd83(bcd+4, sourhi);} + #define GETWCOEFF(df, bcd) { \ + uInt sourhi=DFWWORD(df, 0); \ + uInt sourlo=DFWWORD(df, 1); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>8); \ + dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+7, sourlo>>20); \ + dpd2bcd8(bcd+10, sourlo>>10); \ + dpd2bcd83(bcd+13, sourlo);} + + #elif DECPMAX==16 + #define GETCOEFF(df, bcd) { \ + uInt sourhi=DFWORD(df, 0); \ + uInt sourlo=DFWORD(df, 1); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>8); \ + dpd2bcd8(bcd+4, (sourhi<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+7, sourlo>>20); \ + dpd2bcd8(bcd+10, sourlo>>10); \ + dpd2bcd83(bcd+13, sourlo);} + #define GETWCOEFF(df, bcd) { \ + uInt sourhi=DFWWORD(df, 0); \ + uInt sourmh=DFWWORD(df, 1); \ + uInt sourml=DFWWORD(df, 2); \ + uInt sourlo=DFWWORD(df, 3); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>4); \ + dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \ + dpd2bcd8(bcd+7, sourmh>>16); \ + dpd2bcd8(bcd+10, sourmh>>6); \ + dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \ + dpd2bcd8(bcd+16, sourml>>18); \ + dpd2bcd8(bcd+19, sourml>>8); \ + dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+25, sourlo>>20); \ + dpd2bcd8(bcd+28, sourlo>>10); \ + dpd2bcd83(bcd+31, sourlo);} + + #elif DECPMAX==34 + #define GETCOEFF(df, bcd) { \ + uInt sourhi=DFWORD(df, 0); \ + uInt sourmh=DFWORD(df, 1); \ + uInt sourml=DFWORD(df, 2); \ + uInt sourlo=DFWORD(df, 3); \ + *(bcd)=(uByte)DECCOMBMSD[sourhi>>26]; \ + dpd2bcd8(bcd+1, sourhi>>4); \ + dpd2bcd8(bcd+4, ((sourhi)<<6) | (sourmh>>26)); \ + dpd2bcd8(bcd+7, sourmh>>16); \ + dpd2bcd8(bcd+10, sourmh>>6); \ + dpd2bcd8(bcd+13, ((sourmh)<<4) | (sourml>>28)); \ + dpd2bcd8(bcd+16, sourml>>18); \ + dpd2bcd8(bcd+19, sourml>>8); \ + dpd2bcd8(bcd+22, ((sourml)<<2) | (sourlo>>30)); \ + dpd2bcd8(bcd+25, sourlo>>20); \ + dpd2bcd8(bcd+28, sourlo>>10); \ + dpd2bcd83(bcd+31, sourlo);} + + #define GETWCOEFF(df, bcd) {??} /* [should never be used] */ + #endif + + /* Macros to decode the coefficient in a finite decFloat *df into */ + /* a base-billion uInt array, with the least-significant */ + /* 0-999999999 'digit' at offset 0. */ + + /* Decode the declets. After extracting each one, it is decoded */ + /* to binary using a table lookup. Three tables are used; one */ + /* the usual DPD to binary, the other two pre-multiplied by 1000 */ + /* and 1000000 to avoid multiplication during decode. These */ + /* tables can also be used for multiplying up the MSD as the DPD */ + /* code for 0 through 9 is the identity. */ + #define DPD2BIN0 DPD2BIN /* for prettier code */ + + #if DECPMAX==7 + #define GETCOEFFBILL(df, buf) { \ + uInt sourhi=DFWORD(df, 0); \ + (buf)[0]=DPD2BIN0[sourhi&0x3ff] \ + +DPD2BINK[(sourhi>>10)&0x3ff] \ + +DPD2BINM[DECCOMBMSD[sourhi>>26]];} + + #elif DECPMAX==16 + #define GETCOEFFBILL(df, buf) { \ + uInt sourhi, sourlo; \ + sourlo=DFWORD(df, 1); \ + (buf)[0]=DPD2BIN0[sourlo&0x3ff] \ + +DPD2BINK[(sourlo>>10)&0x3ff] \ + +DPD2BINM[(sourlo>>20)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[1]=DPD2BIN0[((sourhi<<2) | (sourlo>>30))&0x3ff] \ + +DPD2BINK[(sourhi>>8)&0x3ff] \ + +DPD2BINM[DECCOMBMSD[sourhi>>26]];} + + #elif DECPMAX==34 + #define GETCOEFFBILL(df, buf) { \ + uInt sourhi, sourmh, sourml, sourlo; \ + sourlo=DFWORD(df, 3); \ + (buf)[0]=DPD2BIN0[sourlo&0x3ff] \ + +DPD2BINK[(sourlo>>10)&0x3ff] \ + +DPD2BINM[(sourlo>>20)&0x3ff]; \ + sourml=DFWORD(df, 2); \ + (buf)[1]=DPD2BIN0[((sourml<<2) | (sourlo>>30))&0x3ff] \ + +DPD2BINK[(sourml>>8)&0x3ff] \ + +DPD2BINM[(sourml>>18)&0x3ff]; \ + sourmh=DFWORD(df, 1); \ + (buf)[2]=DPD2BIN0[((sourmh<<4) | (sourml>>28))&0x3ff] \ + +DPD2BINK[(sourmh>>6)&0x3ff] \ + +DPD2BINM[(sourmh>>16)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[3]=DPD2BIN0[((sourhi<<6) | (sourmh>>26))&0x3ff] \ + +DPD2BINK[(sourhi>>4)&0x3ff] \ + +DPD2BINM[DECCOMBMSD[sourhi>>26]];} + + #endif + + /* Macros to decode the coefficient in a finite decFloat *df into */ + /* a base-thousand uInt array (of size DECLETS+1, to allow for */ + /* the MSD), with the least-significant 0-999 'digit' at offset 0.*/ + + /* Decode the declets. After extracting each one, it is decoded */ + /* to binary using a table lookup. */ + #if DECPMAX==7 + #define GETCOEFFTHOU(df, buf) { \ + uInt sourhi=DFWORD(df, 0); \ + (buf)[0]=DPD2BIN[sourhi&0x3ff]; \ + (buf)[1]=DPD2BIN[(sourhi>>10)&0x3ff]; \ + (buf)[2]=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==16 + #define GETCOEFFTHOU(df, buf) { \ + uInt sourhi, sourlo; \ + sourlo=DFWORD(df, 1); \ + (buf)[0]=DPD2BIN[sourlo&0x3ff]; \ + (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \ + (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[3]=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \ + (buf)[4]=DPD2BIN[(sourhi>>8)&0x3ff]; \ + (buf)[5]=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==34 + #define GETCOEFFTHOU(df, buf) { \ + uInt sourhi, sourmh, sourml, sourlo; \ + sourlo=DFWORD(df, 3); \ + (buf)[0]=DPD2BIN[sourlo&0x3ff]; \ + (buf)[1]=DPD2BIN[(sourlo>>10)&0x3ff]; \ + (buf)[2]=DPD2BIN[(sourlo>>20)&0x3ff]; \ + sourml=DFWORD(df, 2); \ + (buf)[3]=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \ + (buf)[4]=DPD2BIN[(sourml>>8)&0x3ff]; \ + (buf)[5]=DPD2BIN[(sourml>>18)&0x3ff]; \ + sourmh=DFWORD(df, 1); \ + (buf)[6]=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \ + (buf)[7]=DPD2BIN[(sourmh>>6)&0x3ff]; \ + (buf)[8]=DPD2BIN[(sourmh>>16)&0x3ff]; \ + sourhi=DFWORD(df, 0); \ + (buf)[9]=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \ + (buf)[10]=DPD2BIN[(sourhi>>4)&0x3ff]; \ + (buf)[11]=DECCOMBMSD[sourhi>>26];} + #endif + + + /* Macros to decode the coefficient in a finite decFloat *df and */ + /* add to a base-thousand uInt array (as for GETCOEFFTHOU). */ + /* After the addition then most significant 'digit' in the array */ + /* might have a value larger then 10 (with a maximum of 19). */ + #if DECPMAX==7 + #define ADDCOEFFTHOU(df, buf) { \ + uInt sourhi=DFWORD(df, 0); \ + (buf)[0]+=DPD2BIN[sourhi&0x3ff]; \ + if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ + (buf)[1]+=DPD2BIN[(sourhi>>10)&0x3ff]; \ + if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ + (buf)[2]+=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==16 + #define ADDCOEFFTHOU(df, buf) { \ + uInt sourhi, sourlo; \ + sourlo=DFWORD(df, 1); \ + (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \ + if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ + (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \ + if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ + (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \ + if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \ + sourhi=DFWORD(df, 0); \ + (buf)[3]+=DPD2BIN[((sourhi<<2) | (sourlo>>30))&0x3ff]; \ + if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \ + (buf)[4]+=DPD2BIN[(sourhi>>8)&0x3ff]; \ + if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \ + (buf)[5]+=DECCOMBMSD[sourhi>>26];} + + #elif DECPMAX==34 + #define ADDCOEFFTHOU(df, buf) { \ + uInt sourhi, sourmh, sourml, sourlo; \ + sourlo=DFWORD(df, 3); \ + (buf)[0]+=DPD2BIN[sourlo&0x3ff]; \ + if (buf[0]>999) {buf[0]-=1000; buf[1]++;} \ + (buf)[1]+=DPD2BIN[(sourlo>>10)&0x3ff]; \ + if (buf[1]>999) {buf[1]-=1000; buf[2]++;} \ + (buf)[2]+=DPD2BIN[(sourlo>>20)&0x3ff]; \ + if (buf[2]>999) {buf[2]-=1000; buf[3]++;} \ + sourml=DFWORD(df, 2); \ + (buf)[3]+=DPD2BIN[((sourml<<2) | (sourlo>>30))&0x3ff]; \ + if (buf[3]>999) {buf[3]-=1000; buf[4]++;} \ + (buf)[4]+=DPD2BIN[(sourml>>8)&0x3ff]; \ + if (buf[4]>999) {buf[4]-=1000; buf[5]++;} \ + (buf)[5]+=DPD2BIN[(sourml>>18)&0x3ff]; \ + if (buf[5]>999) {buf[5]-=1000; buf[6]++;} \ + sourmh=DFWORD(df, 1); \ + (buf)[6]+=DPD2BIN[((sourmh<<4) | (sourml>>28))&0x3ff]; \ + if (buf[6]>999) {buf[6]-=1000; buf[7]++;} \ + (buf)[7]+=DPD2BIN[(sourmh>>6)&0x3ff]; \ + if (buf[7]>999) {buf[7]-=1000; buf[8]++;} \ + (buf)[8]+=DPD2BIN[(sourmh>>16)&0x3ff]; \ + if (buf[8]>999) {buf[8]-=1000; buf[9]++;} \ + sourhi=DFWORD(df, 0); \ + (buf)[9]+=DPD2BIN[((sourhi<<6) | (sourmh>>26))&0x3ff]; \ + if (buf[9]>999) {buf[9]-=1000; buf[10]++;} \ + (buf)[10]+=DPD2BIN[(sourhi>>4)&0x3ff]; \ + if (buf[10]>999) {buf[10]-=1000; buf[11]++;} \ + (buf)[11]+=DECCOMBMSD[sourhi>>26];} + #endif + + + /* Set a decFloat to the maximum positive finite number (Nmax) */ + #if DECPMAX==7 + #define DFSETNMAX(df) \ + {DFWORD(df, 0)=0x77f3fcff;} + #elif DECPMAX==16 + #define DFSETNMAX(df) \ + {DFWORD(df, 0)=0x77fcff3f; \ + DFWORD(df, 1)=0xcff3fcff;} + #elif DECPMAX==34 + #define DFSETNMAX(df) \ + {DFWORD(df, 0)=0x77ffcff3; \ + DFWORD(df, 1)=0xfcff3fcf; \ + DFWORD(df, 2)=0xf3fcff3f; \ + DFWORD(df, 3)=0xcff3fcff;} + #endif + + /* [end of format-dependent macros and constants] */ + #endif + +#else + #error decNumberLocal included more than once +#endif diff --git a/extern/decNumber/decPacked.c b/extern/decNumber/decPacked.c new file mode 100644 index 0000000000..90b10715e5 --- /dev/null +++ b/extern/decNumber/decPacked.c @@ -0,0 +1,220 @@ +/* ------------------------------------------------------------------ */ +/* Packed Decimal conversion module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2002. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for Packed Decimal format */ +/* numbers. Conversions are supplied to and from decNumber, which in */ +/* turn supports: */ +/* conversions to and from string */ +/* arithmetic routines */ +/* utilities. */ +/* Conversions from decNumber to and from densely packed decimal */ +/* formats are provided by the decimal32 through decimal128 modules. */ +/* ------------------------------------------------------------------ */ + +#include // for NULL +#include "decNumber.h" // base number library +#include "decPacked.h" // packed decimal +#include "decNumberLocal.h" // decNumber local types, etc. + +/* ------------------------------------------------------------------ */ +/* decPackedFromNumber -- convert decNumber to BCD Packed Decimal */ +/* */ +/* bcd is the BCD bytes */ +/* length is the length of the BCD array */ +/* scale is the scale result */ +/* dn is the decNumber */ +/* returns bcd, or NULL if error */ +/* */ +/* The number is converted to a BCD packed decimal byte array, */ +/* right aligned in the bcd array, whose length is indicated by the */ +/* second parameter. The final 4-bit nibble in the array will be a */ +/* sign nibble, C (1100) for + and D (1101) for -. Unused bytes and */ +/* nibbles to the left of the number are set to 0. */ +/* */ +/* scale is set to the scale of the number (this is the exponent, */ +/* negated). To force the number to a specified scale, first use the */ +/* decNumberRescale routine, which will round and change the exponent */ +/* as necessary. */ +/* */ +/* If there is an error (that is, the decNumber has too many digits */ +/* to fit in length bytes, or it is a NaN or Infinity), NULL is */ +/* returned and the bcd and scale results are unchanged. Otherwise */ +/* bcd is returned. */ +/* ------------------------------------------------------------------ */ +uByte * decPackedFromNumber(uByte *bcd, Int length, Int *scale, + const decNumber *dn) { + const Unit *up=dn->lsu; // Unit array pointer + uByte obyte, *out; // current output byte, and where it goes + Int indigs=dn->digits; // digits processed + uInt cut=DECDPUN; // downcounter per Unit + uInt u=*up; // work + uInt nib; // .. + #if DECDPUN<=4 + uInt temp; // .. + #endif + + if (dn->digits>length*2-1 // too long .. + ||(dn->bits & DECSPECIAL)) return NULL; // .. or special -- hopeless + + if (dn->bits&DECNEG) obyte=DECPMINUS; // set the sign .. + else obyte=DECPPLUS; + *scale=-dn->exponent; // .. and scale + + // loop from lowest (rightmost) byte + out=bcd+length-1; // -> final byte + for (; out>=bcd; out--) { + if (indigs>0) { + if (cut==0) { + up++; + u=*up; + cut=DECDPUN; + } + #if DECDPUN<=4 + temp=(u*6554)>>16; // fast /10 + nib=u-X10(temp); + u=temp; + #else + nib=u%10; // cannot use *6554 trick :-( + u=u/10; + #endif + obyte|=(nib<<4); + indigs--; + cut--; + } + *out=obyte; + obyte=0; // assume 0 + if (indigs>0) { + if (cut==0) { + up++; + u=*up; + cut=DECDPUN; + } + #if DECDPUN<=4 + temp=(u*6554)>>16; // as above + obyte=(uByte)(u-X10(temp)); + u=temp; + #else + obyte=(uByte)(u%10); + u=u/10; + #endif + indigs--; + cut--; + } + } // loop + + return bcd; + } // decPackedFromNumber + +/* ------------------------------------------------------------------ */ +/* decPackedToNumber -- convert BCD Packed Decimal to a decNumber */ +/* */ +/* bcd is the BCD bytes */ +/* length is the length of the BCD array */ +/* scale is the scale associated with the BCD integer */ +/* dn is the decNumber [with space for length*2 digits] */ +/* returns dn, or NULL if error */ +/* */ +/* The BCD packed decimal byte array, together with an associated */ +/* scale, is converted to a decNumber. The BCD array is assumed full */ +/* of digits, and must be ended by a 4-bit sign nibble in the least */ +/* significant four bits of the final byte. */ +/* */ +/* The scale is used (negated) as the exponent of the decNumber. */ +/* Note that zeros may have a sign and/or a scale. */ +/* */ +/* The decNumber structure is assumed to have sufficient space to */ +/* hold the converted number (that is, up to length*2-1 digits), so */ +/* no error is possible unless the adjusted exponent is out of range, */ +/* no sign nibble was found, or a sign nibble was found before the */ +/* final nibble. In these error cases, NULL is returned and the */ +/* decNumber will be 0. */ +/* ------------------------------------------------------------------ */ +decNumber * decPackedToNumber(const uByte *bcd, Int length, + const Int *scale, decNumber *dn) { + const uByte *last=bcd+length-1; // -> last byte + const uByte *first; // -> first non-zero byte + uInt nib; // work nibble + Unit *up=dn->lsu; // output pointer + Int digits; // digits count + Int cut=0; // phase of output + + decNumberZero(dn); // default result + last=&bcd[length-1]; + nib=*last & 0x0f; // get the sign + if (nib==DECPMINUS || nib==DECPMINUSALT) dn->bits=DECNEG; + else if (nib<=9) return NULL; // not a sign nibble + + // skip leading zero bytes [final byte is always non-zero, due to sign] + for (first=bcd; *first==0;) first++; + digits=(last-first)*2+1; // calculate digits .. + if ((*first & 0xf0)==0) digits--; // adjust for leading zero nibble + if (digits!=0) dn->digits=digits; // count of actual digits [if 0, + // leave as 1] + + // check the adjusted exponent; note that scale could be unbounded + dn->exponent=-*scale; // set the exponent + if (*scale>=0) { // usual case + if ((dn->digits-*scale-1)<-DECNUMMAXE) { // underflow + decNumberZero(dn); + return NULL;} + } + else { // -ve scale; +ve exponent + // need to be careful to avoid wrap, here, also BADINT case + if ((*scale<-DECNUMMAXE) // overflow even without digits + || ((dn->digits-*scale-1)>DECNUMMAXE)) { // overflow + decNumberZero(dn); + return NULL;} + } + if (digits==0) return dn; // result was zero + + // copy the digits to the number's units, starting at the lsu + // [unrolled] + for (;;) { // forever + // left nibble first + nib=(unsigned)(*last & 0xf0)>>4; + // got a digit, in nib + if (nib>9) {decNumberZero(dn); return NULL;} + + if (cut==0) *up=(Unit)nib; + else *up=(Unit)(*up+nib*DECPOWERS[cut]); + digits--; + if (digits==0) break; // got them all + cut++; + if (cut==DECDPUN) { + up++; + cut=0; + } + last--; // ready for next + nib=*last & 0x0f; // get right nibble + if (nib>9) {decNumberZero(dn); return NULL;} + + // got a digit, in nib + if (cut==0) *up=(Unit)nib; + else *up=(Unit)(*up+nib*DECPOWERS[cut]); + digits--; + if (digits==0) break; // got them all + cut++; + if (cut==DECDPUN) { + up++; + cut=0; + } + } // forever + + return dn; + } // decPackedToNumber + diff --git a/extern/decNumber/decPacked.h b/extern/decNumber/decPacked.h new file mode 100644 index 0000000000..929a546253 --- /dev/null +++ b/extern/decNumber/decPacked.h @@ -0,0 +1,52 @@ +/* ------------------------------------------------------------------ */ +/* Packed Decimal conversion module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2005. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECPACKED) + #define DECPACKED + #define DECPNAME "decPacked" /* Short name */ + #define DECPFULLNAME "Packed Decimal conversions" /* Verbose name */ + #define DECPAUTHOR "Mike Cowlishaw" /* Who to blame */ + + #define DECPACKED_DefP 32 /* default precision */ + + #ifndef DECNUMDIGITS + #define DECNUMDIGITS DECPACKED_DefP /* size if not already defined*/ + #endif + #include "decNumber.h" /* context and number library */ + + /* Sign nibble constants */ + #if !defined(DECPPLUSALT) + #define DECPPLUSALT 0x0A /* alternate plus nibble */ + #define DECPMINUSALT 0x0B /* alternate minus nibble */ + #define DECPPLUS 0x0C /* preferred plus nibble */ + #define DECPMINUS 0x0D /* preferred minus nibble */ + #define DECPPLUSALT2 0x0E /* alternate plus nibble */ + #define DECPUNSIGNED 0x0F /* alternate plus nibble (unsigned) */ + #endif + + /* ---------------------------------------------------------------- */ + /* decPacked public routines */ + /* ---------------------------------------------------------------- */ + /* Conversions */ + uint8_t * decPackedFromNumber(uint8_t *, int32_t, int32_t *, + const decNumber *); + decNumber * decPackedToNumber(const uint8_t *, int32_t, const int32_t *, + decNumber *); + +#endif diff --git a/extern/decNumber/decQuad.c b/extern/decNumber/decQuad.c new file mode 100644 index 0000000000..c65b30c2af --- /dev/null +++ b/extern/decNumber/decQuad.c @@ -0,0 +1,135 @@ +/* ------------------------------------------------------------------ */ +/* decQuad.c -- decQuad operations module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises decQuad operations (including conversions) */ +/* ------------------------------------------------------------------ */ + + +/* Constant mappings for shared code */ +#define DECPMAX DECQUAD_Pmax +#define DECEMIN DECQUAD_Emin +#define DECEMAX DECQUAD_Emax +#define DECEMAXD DECQUAD_EmaxD +#define DECBYTES DECQUAD_Bytes +#define DECSTRING DECQUAD_String +#define DECECONL DECQUAD_EconL +#define DECBIAS DECQUAD_Bias +#define DECLETS DECQUAD_Declets +#define DECQTINY (-DECQUAD_Bias) + +/* Type and function mappings for shared code */ +#define decFloat decQuad // Type name + +// Utilities and conversions (binary results, extractors, etc.) +#define decFloatFromBCD decQuadFromBCD +#define decFloatFromInt32 decQuadFromInt32 +#define decFloatFromPacked decQuadFromPacked +#define decFloatFromPackedChecked decQuadFromPackedChecked +#define decFloatFromString decQuadFromString +#define decFloatFromUInt32 decQuadFromUInt32 +#define decFloatFromWider decQuadFromWider +#define decFloatGetCoefficient decQuadGetCoefficient +#define decFloatGetExponent decQuadGetExponent +#define decFloatSetCoefficient decQuadSetCoefficient +#define decFloatSetExponent decQuadSetExponent +#define decFloatShow decQuadShow +#define decFloatToBCD decQuadToBCD +#define decFloatToEngString decQuadToEngString +#define decFloatToInt32 decQuadToInt32 +#define decFloatToInt32Exact decQuadToInt32Exact +#define decFloatToPacked decQuadToPacked +#define decFloatToString decQuadToString +#define decFloatToUInt32 decQuadToUInt32 +#define decFloatToUInt32Exact decQuadToUInt32Exact +#define decFloatToWider decQuadToWider +#define decFloatZero decQuadZero + +// Computational (result is a decFloat) +#define decFloatAbs decQuadAbs +#define decFloatAdd decQuadAdd +#define decFloatAnd decQuadAnd +#define decFloatDivide decQuadDivide +#define decFloatDivideInteger decQuadDivideInteger +#define decFloatFMA decQuadFMA +#define decFloatInvert decQuadInvert +#define decFloatLogB decQuadLogB +#define decFloatMax decQuadMax +#define decFloatMaxMag decQuadMaxMag +#define decFloatMin decQuadMin +#define decFloatMinMag decQuadMinMag +#define decFloatMinus decQuadMinus +#define decFloatMultiply decQuadMultiply +#define decFloatNextMinus decQuadNextMinus +#define decFloatNextPlus decQuadNextPlus +#define decFloatNextToward decQuadNextToward +#define decFloatOr decQuadOr +#define decFloatPlus decQuadPlus +#define decFloatQuantize decQuadQuantize +#define decFloatReduce decQuadReduce +#define decFloatRemainder decQuadRemainder +#define decFloatRemainderNear decQuadRemainderNear +#define decFloatRotate decQuadRotate +#define decFloatScaleB decQuadScaleB +#define decFloatShift decQuadShift +#define decFloatSubtract decQuadSubtract +#define decFloatToIntegralValue decQuadToIntegralValue +#define decFloatToIntegralExact decQuadToIntegralExact +#define decFloatXor decQuadXor + +// Comparisons +#define decFloatCompare decQuadCompare +#define decFloatCompareSignal decQuadCompareSignal +#define decFloatCompareTotal decQuadCompareTotal +#define decFloatCompareTotalMag decQuadCompareTotalMag + +// Copies +#define decFloatCanonical decQuadCanonical +#define decFloatCopy decQuadCopy +#define decFloatCopyAbs decQuadCopyAbs +#define decFloatCopyNegate decQuadCopyNegate +#define decFloatCopySign decQuadCopySign + +// Non-computational +#define decFloatClass decQuadClass +#define decFloatClassString decQuadClassString +#define decFloatDigits decQuadDigits +#define decFloatIsCanonical decQuadIsCanonical +#define decFloatIsFinite decQuadIsFinite +#define decFloatIsInfinite decQuadIsInfinite +#define decFloatIsInteger decQuadIsInteger +#define decFloatIsLogical decQuadIsLogical +#define decFloatIsNaN decQuadIsNaN +#define decFloatIsNegative decQuadIsNegative +#define decFloatIsNormal decQuadIsNormal +#define decFloatIsPositive decQuadIsPositive +#define decFloatIsSignaling decQuadIsSignaling +#define decFloatIsSignalling decQuadIsSignalling +#define decFloatIsSigned decQuadIsSigned +#define decFloatIsSubnormal decQuadIsSubnormal +#define decFloatIsZero decQuadIsZero +#define decFloatRadix decQuadRadix +#define decFloatSameQuantum decQuadSameQuantum +#define decFloatVersion decQuadVersion + +/* And now the code itself */ +#include "decContext.h" // public includes +#include "decQuad.h" // .. +#include "decNumberLocal.h" // local includes (need DECPMAX) +#include "decCommon.c" // non-arithmetic decFloat routines +#include "decBasic.c" // basic formats routines + diff --git a/extern/decNumber/decQuad.h b/extern/decNumber/decQuad.h new file mode 100644 index 0000000000..829f39a0ba --- /dev/null +++ b/extern/decNumber/decQuad.h @@ -0,0 +1,177 @@ +/* ------------------------------------------------------------------ */ +/* decQuad.h -- Decimal 128-bit format module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2010. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This include file is always included by decSingle and decDouble, */ +/* and therefore also holds useful constants used by all three. */ + +#if !defined(DECQUAD) + #define DECQUAD + + #define DECQUADNAME "decimalQuad" /* Short name */ + #define DECQUADTITLE "Decimal 128-bit datum" /* Verbose name */ + #define DECQUADAUTHOR "Mike Cowlishaw" /* Who to blame */ + + /* parameters for decQuads */ + #define DECQUAD_Bytes 16 /* length */ + #define DECQUAD_Pmax 34 /* maximum precision (digits) */ + #define DECQUAD_Emin -6143 /* minimum adjusted exponent */ + #define DECQUAD_Emax 6144 /* maximum adjusted exponent */ + #define DECQUAD_EmaxD 4 /* maximum exponent digits */ + #define DECQUAD_Bias 6176 /* bias for the exponent */ + #define DECQUAD_String 43 /* maximum string length, +1 */ + #define DECQUAD_EconL 12 /* exponent continuation length */ + #define DECQUAD_Declets 11 /* count of declets */ + /* highest biased exponent (Elimit-1) */ + #define DECQUAD_Ehigh (DECQUAD_Emax + DECQUAD_Bias - (DECQUAD_Pmax-1)) + + /* Required include */ + #include "decContext.h" + + /* The decQuad decimal 128-bit type, accessible by all sizes */ + typedef union { + uint8_t bytes[DECQUAD_Bytes]; /* fields: 1, 5, 12, 110 bits */ + uint16_t shorts[DECQUAD_Bytes/2]; + uint32_t words[DECQUAD_Bytes/4]; + #if DECUSE64 + uint64_t longs[DECQUAD_Bytes/8]; + #endif + } decQuad; + + /* ---------------------------------------------------------------- */ + /* Shared constants */ + /* ---------------------------------------------------------------- */ + + /* sign and special values [top 32-bits; last two bits are don't-care + for Infinity on input, last bit don't-care for NaNs] */ + #define DECFLOAT_Sign 0x80000000 /* 1 00000 00 Sign */ + #define DECFLOAT_NaN 0x7c000000 /* 0 11111 00 NaN generic */ + #define DECFLOAT_qNaN 0x7c000000 /* 0 11111 00 qNaN */ + #define DECFLOAT_sNaN 0x7e000000 /* 0 11111 10 sNaN */ + #define DECFLOAT_Inf 0x78000000 /* 0 11110 00 Infinity */ + #define DECFLOAT_MinSp 0x78000000 /* minimum special value */ + /* [specials are all >=MinSp] */ + /* Sign nibble constants */ + #if !defined(DECPPLUSALT) + #define DECPPLUSALT 0x0A /* alternate plus nibble */ + #define DECPMINUSALT 0x0B /* alternate minus nibble */ + #define DECPPLUS 0x0C /* preferred plus nibble */ + #define DECPMINUS 0x0D /* preferred minus nibble */ + #define DECPPLUSALT2 0x0E /* alternate plus nibble */ + #define DECPUNSIGNED 0x0F /* alternate plus nibble (unsigned) */ + #endif + + /* ---------------------------------------------------------------- */ + /* Routines -- implemented as decFloat routines in common files */ + /* ---------------------------------------------------------------- */ + + /* Utilities and conversions, extractors, etc.) */ + extern decQuad * decQuadFromBCD(decQuad *, int32_t, const uint8_t *, int32_t); + extern decQuad * decQuadFromInt32(decQuad *, int32_t); + extern decQuad * decQuadFromPacked(decQuad *, int32_t, const uint8_t *); + extern decQuad * decQuadFromPackedChecked(decQuad *, int32_t, const uint8_t *); + extern decQuad * decQuadFromString(decQuad *, const char *, decContext *); + extern decQuad * decQuadFromUInt32(decQuad *, uint32_t); + extern int32_t decQuadGetCoefficient(const decQuad *, uint8_t *); + extern int32_t decQuadGetExponent(const decQuad *); + extern decQuad * decQuadSetCoefficient(decQuad *, const uint8_t *, int32_t); + extern decQuad * decQuadSetExponent(decQuad *, decContext *, int32_t); + extern void decQuadShow(const decQuad *, const char *); + extern int32_t decQuadToBCD(const decQuad *, int32_t *, uint8_t *); + extern char * decQuadToEngString(const decQuad *, char *); + extern int32_t decQuadToInt32(const decQuad *, decContext *, enum rounding); + extern int32_t decQuadToInt32Exact(const decQuad *, decContext *, enum rounding); + extern int32_t decQuadToPacked(const decQuad *, int32_t *, uint8_t *); + extern char * decQuadToString(const decQuad *, char *); + extern uint32_t decQuadToUInt32(const decQuad *, decContext *, enum rounding); + extern uint32_t decQuadToUInt32Exact(const decQuad *, decContext *, enum rounding); + extern decQuad * decQuadZero(decQuad *); + + /* Computational (result is a decQuad) */ + extern decQuad * decQuadAbs(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadAdd(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadAnd(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadDivide(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadDivideInteger(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadFMA(decQuad *, const decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadInvert(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadLogB(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadMax(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadMaxMag(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadMin(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadMinMag(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadMinus(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadMultiply(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadNextMinus(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadNextPlus(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadNextToward(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadOr(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadPlus(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadQuantize(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadReduce(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadRemainder(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadRemainderNear(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadRotate(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadScaleB(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadShift(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadSubtract(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadToIntegralValue(decQuad *, const decQuad *, decContext *, enum rounding); + extern decQuad * decQuadToIntegralExact(decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadXor(decQuad *, const decQuad *, const decQuad *, decContext *); + + /* Comparisons */ + extern decQuad * decQuadCompare(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadCompareSignal(decQuad *, const decQuad *, const decQuad *, decContext *); + extern decQuad * decQuadCompareTotal(decQuad *, const decQuad *, const decQuad *); + extern decQuad * decQuadCompareTotalMag(decQuad *, const decQuad *, const decQuad *); + + /* Copies */ + extern decQuad * decQuadCanonical(decQuad *, const decQuad *); + extern decQuad * decQuadCopy(decQuad *, const decQuad *); + extern decQuad * decQuadCopyAbs(decQuad *, const decQuad *); + extern decQuad * decQuadCopyNegate(decQuad *, const decQuad *); + extern decQuad * decQuadCopySign(decQuad *, const decQuad *, const decQuad *); + + /* Non-computational */ + extern enum decClass decQuadClass(const decQuad *); + extern const char * decQuadClassString(const decQuad *); + extern uint32_t decQuadDigits(const decQuad *); + extern uint32_t decQuadIsCanonical(const decQuad *); + extern uint32_t decQuadIsFinite(const decQuad *); + extern uint32_t decQuadIsInteger(const decQuad *); + extern uint32_t decQuadIsLogical(const decQuad *); + extern uint32_t decQuadIsInfinite(const decQuad *); + extern uint32_t decQuadIsNaN(const decQuad *); + extern uint32_t decQuadIsNegative(const decQuad *); + extern uint32_t decQuadIsNormal(const decQuad *); + extern uint32_t decQuadIsPositive(const decQuad *); + extern uint32_t decQuadIsSignaling(const decQuad *); + extern uint32_t decQuadIsSignalling(const decQuad *); + extern uint32_t decQuadIsSigned(const decQuad *); + extern uint32_t decQuadIsSubnormal(const decQuad *); + extern uint32_t decQuadIsZero(const decQuad *); + extern uint32_t decQuadRadix(const decQuad *); + extern uint32_t decQuadSameQuantum(const decQuad *, const decQuad *); + extern const char * decQuadVersion(void); + + /* decNumber conversions; these are implemented as macros so as not */ + /* to force a dependency on decimal128 and decNumber in decQuad. */ + /* decQuadFromNumber returns a decimal128 * to avoid warnings. */ + #define decQuadToNumber(dq, dn) decimal128ToNumber((decimal128 *)(dq), dn) + #define decQuadFromNumber(dq, dn, set) decimal128FromNumber((decimal128 *)(dq), dn, set) + +#endif diff --git a/extern/decNumber/decSingle.c b/extern/decNumber/decSingle.c new file mode 100644 index 0000000000..85e59d5a14 --- /dev/null +++ b/extern/decNumber/decSingle.c @@ -0,0 +1,71 @@ +/* ------------------------------------------------------------------ */ +/* decSingle.c -- decSingle operations module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises decSingle operations (including conversions) */ +/* ------------------------------------------------------------------ */ + +#include "decContext.h" // public includes +#include "decSingle.h" // public includes + +/* Constant mappings for shared code */ +#define DECPMAX DECSINGLE_Pmax +#define DECEMIN DECSINGLE_Emin +#define DECEMAX DECSINGLE_Emax +#define DECEMAXD DECSINGLE_EmaxD +#define DECBYTES DECSINGLE_Bytes +#define DECSTRING DECSINGLE_String +#define DECECONL DECSINGLE_EconL +#define DECBIAS DECSINGLE_Bias +#define DECLETS DECSINGLE_Declets +#define DECQTINY (-DECSINGLE_Bias) +// parameters of next-wider format +#define DECWBYTES DECDOUBLE_Bytes +#define DECWPMAX DECDOUBLE_Pmax +#define DECWECONL DECDOUBLE_EconL +#define DECWBIAS DECDOUBLE_Bias + +/* Type and function mappings for shared code */ +#define decFloat decSingle // Type name +#define decFloatWider decDouble // Type name + +// Utility (binary results, extractors, etc.) +#define decFloatFromBCD decSingleFromBCD +#define decFloatFromPacked decSingleFromPacked +#define decFloatFromPackedChecked decSingleFromPackedChecked +#define decFloatFromString decSingleFromString +#define decFloatFromWider decSingleFromWider +#define decFloatGetCoefficient decSingleGetCoefficient +#define decFloatGetExponent decSingleGetExponent +#define decFloatSetCoefficient decSingleSetCoefficient +#define decFloatSetExponent decSingleSetExponent +#define decFloatShow decSingleShow +#define decFloatToBCD decSingleToBCD +#define decFloatToEngString decSingleToEngString +#define decFloatToPacked decSingleToPacked +#define decFloatToString decSingleToString +#define decFloatToWider decSingleToWider +#define decFloatZero decSingleZero + +// Non-computational +#define decFloatRadix decSingleRadix +#define decFloatVersion decSingleVersion + +#include "decNumberLocal.h" // local includes (need DECPMAX) +#include "decCommon.c" // non-basic decFloat routines +// [Do not include decBasic.c for decimal32] + diff --git a/extern/decNumber/decSingle.h b/extern/decNumber/decSingle.h new file mode 100644 index 0000000000..2bd1fdecb0 --- /dev/null +++ b/extern/decNumber/decSingle.h @@ -0,0 +1,86 @@ +/* ------------------------------------------------------------------ */ +/* decSingle.h -- Decimal 32-bit format module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is included in the package as decNumber.pdf. This */ +/* document is also available in HTML, together with specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECSINGLE) + #define DECSINGLE + + #define DECSINGLENAME "decSingle" /* Short name */ + #define DECSINGLETITLE "Decimal 32-bit datum" /* Verbose name */ + #define DECSINGLEAUTHOR "Mike Cowlishaw" /* Who to blame */ + + /* parameters for decSingles */ + #define DECSINGLE_Bytes 4 /* length */ + #define DECSINGLE_Pmax 7 /* maximum precision (digits) */ + #define DECSINGLE_Emin -95 /* minimum adjusted exponent */ + #define DECSINGLE_Emax 96 /* maximum adjusted exponent */ + #define DECSINGLE_EmaxD 3 /* maximum exponent digits */ + #define DECSINGLE_Bias 101 /* bias for the exponent */ + #define DECSINGLE_String 16 /* maximum string length, +1 */ + #define DECSINGLE_EconL 6 /* exponent continuation length */ + #define DECSINGLE_Declets 2 /* count of declets */ + /* highest biased exponent (Elimit-1) */ + #define DECSINGLE_Ehigh (DECSINGLE_Emax + DECSINGLE_Bias - (DECSINGLE_Pmax-1)) + + /* Required includes */ + #include "decContext.h" + #include "decQuad.h" + #include "decDouble.h" + + /* The decSingle decimal 32-bit type, accessible by all sizes */ + typedef union { + uint8_t bytes[DECSINGLE_Bytes]; /* fields: 1, 5, 6, 20 bits */ + uint16_t shorts[DECSINGLE_Bytes/2]; + uint32_t words[DECSINGLE_Bytes/4]; + } decSingle; + + /* ---------------------------------------------------------------- */ + /* Routines -- implemented as decFloat routines in common files */ + /* ---------------------------------------------------------------- */ + + /* Utilities (binary argument(s) or result, extractors, etc.) */ + extern decSingle * decSingleFromBCD(decSingle *, int32_t, const uint8_t *, int32_t); + extern decSingle * decSingleFromPacked(decSingle *, int32_t, const uint8_t *); + extern decSingle * decSingleFromPackedChecked(decSingle *, int32_t, const uint8_t *); + extern decSingle * decSingleFromString(decSingle *, const char *, decContext *); + extern decSingle * decSingleFromWider(decSingle *, const decDouble *, decContext *); + extern int32_t decSingleGetCoefficient(const decSingle *, uint8_t *); + extern int32_t decSingleGetExponent(const decSingle *); + extern decSingle * decSingleSetCoefficient(decSingle *, const uint8_t *, int32_t); + extern decSingle * decSingleSetExponent(decSingle *, decContext *, int32_t); + extern void decSingleShow(const decSingle *, const char *); + extern int32_t decSingleToBCD(const decSingle *, int32_t *, uint8_t *); + extern char * decSingleToEngString(const decSingle *, char *); + extern int32_t decSingleToPacked(const decSingle *, int32_t *, uint8_t *); + extern char * decSingleToString(const decSingle *, char *); + extern decDouble * decSingleToWider(const decSingle *, decDouble *); + extern decSingle * decSingleZero(decSingle *); + + /* (No Arithmetic routines for decSingle) */ + + /* Non-computational */ + extern uint32_t decSingleRadix(const decSingle *); + extern const char * decSingleVersion(void); + + /* decNumber conversions; these are implemented as macros so as not */ + /* to force a dependency on decimal32 and decNumber in decSingle. */ + /* decSingleFromNumber returns a decimal32 * to avoid warnings. */ + #define decSingleToNumber(dq, dn) decimal32ToNumber((decimal32 *)(dq), dn) + #define decSingleFromNumber(dq, dn, set) decimal32FromNumber((decimal32 *)(dq), dn, set) + +#endif diff --git a/extern/decNumber/decimal128.c b/extern/decNumber/decimal128.c new file mode 100644 index 0000000000..4387f4479b --- /dev/null +++ b/extern/decNumber/decimal128.c @@ -0,0 +1,553 @@ +/* ------------------------------------------------------------------ */ +/* Decimal 128-bit format module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for decimal128 format numbers. */ +/* Conversions are supplied to and from decNumber and String. */ +/* */ +/* This is used when decNumber provides operations, either for all */ +/* operations or as a proxy between decNumber and decSingle. */ +/* */ +/* Error handling is the same as decNumber (qv.). */ +/* ------------------------------------------------------------------ */ +#include // [for memset/memcpy] +#include // [for printf] + +#define DECNUMDIGITS 34 // make decNumbers with space for 34 +#include "decNumber.h" // base number library +#include "decNumberLocal.h" // decNumber local types, etc. +#include "decimal128.h" // our primary include + +/* Utility routines and tables [in decimal64.c] */ +// DPD2BIN and the reverse are renamed to prevent link-time conflict +// if decQuad is also built in the same executable +#define DPD2BIN DPD2BINx +#define BIN2DPD BIN2DPDx +extern const uInt COMBEXP[32], COMBMSD[32]; +extern const uShort DPD2BIN[1024]; +extern const uShort BIN2DPD[1000]; // [not used] +extern const uByte BIN2CHAR[4001]; + +extern void decDigitsFromDPD(decNumber *, const uInt *, Int); +extern void decDigitsToDPD(const decNumber *, uInt *, Int); + +#if DECTRACE || DECCHECK +void decimal128Show(const decimal128 *); // for debug +extern void decNumberShow(const decNumber *); // .. +#endif + +/* Useful macro */ +// Clear a structure (e.g., a decNumber) +#define DEC_clear(d) memset(d, 0, sizeof(*d)) + +/* ------------------------------------------------------------------ */ +/* decimal128FromNumber -- convert decNumber to decimal128 */ +/* */ +/* ds is the target decimal128 */ +/* dn is the source number (assumed valid) */ +/* set is the context, used only for reporting errors */ +/* */ +/* The set argument is used only for status reporting and for the */ +/* rounding mode (used if the coefficient is more than DECIMAL128_Pmax*/ +/* digits or an overflow is detected). If the exponent is out of the */ +/* valid range then Overflow or Underflow will be raised. */ +/* After Underflow a subnormal result is possible. */ +/* */ +/* DEC_Clamped is set if the number has to be 'folded down' to fit, */ +/* by reducing its exponent and multiplying the coefficient by a */ +/* power of ten, or if the exponent on a zero had to be clamped. */ +/* ------------------------------------------------------------------ */ +decimal128 * decimal128FromNumber(decimal128 *d128, const decNumber *dn, + decContext *set) { + uInt status=0; // status accumulator + Int ae; // adjusted exponent + decNumber dw; // work + decContext dc; // .. + uInt comb, exp; // .. + uInt uiwork; // for macros + uInt targar[4]={0,0,0,0}; // target 128-bit + #define targhi targar[3] // name the word with the sign + #define targmh targar[2] // name the words + #define targml targar[1] // .. + #define targlo targar[0] // .. + + // If the number has too many digits, or the exponent could be + // out of range then reduce the number under the appropriate + // constraints. This could push the number to Infinity or zero, + // so this check and rounding must be done before generating the + // decimal128] + ae=dn->exponent+dn->digits-1; // [0 if special] + if (dn->digits>DECIMAL128_Pmax // too many digits + || ae>DECIMAL128_Emax // likely overflow + || aeround; // use supplied rounding + decNumberPlus(&dw, dn, &dc); // (round and check) + // [this changes -0 to 0, so enforce the sign...] + dw.bits|=dn->bits&DECNEG; + status=dc.status; // save status + dn=&dw; // use the work number + } // maybe out of range + + if (dn->bits&DECSPECIAL) { // a special value + if (dn->bits&DECINF) targhi=DECIMAL_Inf<<24; + else { // sNaN or qNaN + if ((*dn->lsu!=0 || dn->digits>1) // non-zero coefficient + && (dn->digitsbits&DECNAN) targhi|=DECIMAL_NaN<<24; + else targhi|=DECIMAL_sNaN<<24; + } // a NaN + } // special + + else { // is finite + if (decNumberIsZero(dn)) { // is a zero + // set and clamp exponent + if (dn->exponent<-DECIMAL128_Bias) { + exp=0; // low clamp + status|=DEC_Clamped; + } + else { + exp=dn->exponent+DECIMAL128_Bias; // bias exponent + if (exp>DECIMAL128_Ehigh) { // top clamp + exp=DECIMAL128_Ehigh; + status|=DEC_Clamped; + } + } + comb=(exp>>9) & 0x18; // msd=0, exp top 2 bits .. + } + else { // non-zero finite number + uInt msd; // work + Int pad=0; // coefficient pad digits + + // the dn is known to fit, but it may need to be padded + exp=(uInt)(dn->exponent+DECIMAL128_Bias); // bias exponent + if (exp>DECIMAL128_Ehigh) { // fold-down case + pad=exp-DECIMAL128_Ehigh; + exp=DECIMAL128_Ehigh; // [to maximum] + status|=DEC_Clamped; + } + + // [fastpath for common case is not a win, here] + decDigitsToDPD(dn, targar, pad); + // save and clear the top digit + msd=targhi>>14; + targhi&=0x00003fff; + + // create the combination field + if (msd>=8) comb=0x18 | ((exp>>11) & 0x06) | (msd & 0x01); + else comb=((exp>>9) & 0x18) | msd; + } + targhi|=comb<<26; // add combination field .. + targhi|=(exp&0xfff)<<14; // .. and exponent continuation + } // finite + + if (dn->bits&DECNEG) targhi|=0x80000000; // add sign bit + + // now write to storage; this is endian + if (DECLITEND) { + // lo -> hi + UBFROMUI(d128->bytes, targlo); + UBFROMUI(d128->bytes+4, targml); + UBFROMUI(d128->bytes+8, targmh); + UBFROMUI(d128->bytes+12, targhi); + } + else { + // hi -> lo + UBFROMUI(d128->bytes, targhi); + UBFROMUI(d128->bytes+4, targmh); + UBFROMUI(d128->bytes+8, targml); + UBFROMUI(d128->bytes+12, targlo); + } + + if (status!=0) decContextSetStatus(set, status); // pass on status + // decimal128Show(d128); + return d128; + } // decimal128FromNumber + +/* ------------------------------------------------------------------ */ +/* decimal128ToNumber -- convert decimal128 to decNumber */ +/* d128 is the source decimal128 */ +/* dn is the target number, with appropriate space */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decNumber * decimal128ToNumber(const decimal128 *d128, decNumber *dn) { + uInt msd; // coefficient MSD + uInt exp; // exponent top two bits + uInt comb; // combination field + Int need; // work + uInt uiwork; // for macros + uInt sourar[4]; // source 128-bit + #define sourhi sourar[3] // name the word with the sign + #define sourmh sourar[2] // and the mid-high word + #define sourml sourar[1] // and the mod-low word + #define sourlo sourar[0] // and the lowest word + + // load source from storage; this is endian + if (DECLITEND) { + sourlo=UBTOUI(d128->bytes ); // directly load the low int + sourml=UBTOUI(d128->bytes+4 ); // then the mid-low + sourmh=UBTOUI(d128->bytes+8 ); // then the mid-high + sourhi=UBTOUI(d128->bytes+12); // then the high int + } + else { + sourhi=UBTOUI(d128->bytes ); // directly load the high int + sourmh=UBTOUI(d128->bytes+4 ); // then the mid-high + sourml=UBTOUI(d128->bytes+8 ); // then the mid-low + sourlo=UBTOUI(d128->bytes+12); // then the low int + } + + comb=(sourhi>>26)&0x1f; // combination field + + decNumberZero(dn); // clean number + if (sourhi&0x80000000) dn->bits=DECNEG; // set sign if negative + + msd=COMBMSD[comb]; // decode the combination field + exp=COMBEXP[comb]; // .. + + if (exp==3) { // is a special + if (msd==0) { + dn->bits|=DECINF; + return dn; // no coefficient needed + } + else if (sourhi&0x02000000) dn->bits|=DECSNAN; + else dn->bits|=DECNAN; + msd=0; // no top digit + } + else { // is a finite number + dn->exponent=(exp<<12)+((sourhi>>14)&0xfff)-DECIMAL128_Bias; // unbiased + } + + // get the coefficient + sourhi&=0x00003fff; // clean coefficient continuation + if (msd) { // non-zero msd + sourhi|=msd<<14; // prefix to coefficient + need=12; // process 12 declets + } + else { // msd=0 + if (sourhi) need=11; // declets to process + else if (sourmh) need=10; + else if (sourml) need=7; + else if (sourlo) need=4; + else return dn; // easy: coefficient is 0 + } //msd=0 + + decDigitsFromDPD(dn, sourar, need); // process declets + // decNumberShow(dn); + return dn; + } // decimal128ToNumber + +/* ------------------------------------------------------------------ */ +/* to-scientific-string -- conversion to numeric string */ +/* to-engineering-string -- conversion to numeric string */ +/* */ +/* decimal128ToString(d128, string); */ +/* decimal128ToEngString(d128, string); */ +/* */ +/* d128 is the decimal128 format number to convert */ +/* string is the string where the result will be laid out */ +/* */ +/* string must be at least 24 characters */ +/* */ +/* No error is possible, and no status can be set. */ +/* ------------------------------------------------------------------ */ +char * decimal128ToEngString(const decimal128 *d128, char *string){ + decNumber dn; // work + decimal128ToNumber(d128, &dn); + decNumberToEngString(&dn, string); + return string; + } // decimal128ToEngString + +char * decimal128ToString(const decimal128 *d128, char *string){ + uInt msd; // coefficient MSD + Int exp; // exponent top two bits or full + uInt comb; // combination field + char *cstart; // coefficient start + char *c; // output pointer in string + const uByte *u; // work + char *s, *t; // .. (source, target) + Int dpd; // .. + Int pre, e; // .. + uInt uiwork; // for macros + + uInt sourar[4]; // source 128-bit + #define sourhi sourar[3] // name the word with the sign + #define sourmh sourar[2] // and the mid-high word + #define sourml sourar[1] // and the mod-low word + #define sourlo sourar[0] // and the lowest word + + // load source from storage; this is endian + if (DECLITEND) { + sourlo=UBTOUI(d128->bytes ); // directly load the low int + sourml=UBTOUI(d128->bytes+4 ); // then the mid-low + sourmh=UBTOUI(d128->bytes+8 ); // then the mid-high + sourhi=UBTOUI(d128->bytes+12); // then the high int + } + else { + sourhi=UBTOUI(d128->bytes ); // directly load the high int + sourmh=UBTOUI(d128->bytes+4 ); // then the mid-high + sourml=UBTOUI(d128->bytes+8 ); // then the mid-low + sourlo=UBTOUI(d128->bytes+12); // then the low int + } + + c=string; // where result will go + if (((Int)sourhi)<0) *c++='-'; // handle sign + + comb=(sourhi>>26)&0x1f; // combination field + msd=COMBMSD[comb]; // decode the combination field + exp=COMBEXP[comb]; // .. + + if (exp==3) { + if (msd==0) { // infinity + strcpy(c, "Inf"); + strcpy(c+3, "inity"); + return string; // easy + } + if (sourhi&0x02000000) *c++='s'; // sNaN + strcpy(c, "NaN"); // complete word + c+=3; // step past + if (sourlo==0 && sourml==0 && sourmh==0 + && (sourhi&0x0003ffff)==0) return string; // zero payload + // otherwise drop through to add integer; set correct exp + exp=0; msd=0; // setup for following code + } + else exp=(exp<<12)+((sourhi>>14)&0xfff)-DECIMAL128_Bias; // unbiased + + // convert 34 digits of significand to characters + cstart=c; // save start of coefficient + if (msd) *c++='0'+(char)msd; // non-zero most significant digit + + // Now decode the declets. After extracting each one, it is + // decoded to binary and then to a 4-char sequence by table lookup; + // the 4-chars are a 1-char length (significant digits, except 000 + // has length 0). This allows us to left-align the first declet + // with non-zero content, then remaining ones are full 3-char + // length. We use fixed-length memcpys because variable-length + // causes a subroutine call in GCC. (These are length 4 for speed + // and are safe because the array has an extra terminator byte.) + #define dpd2char u=&BIN2CHAR[DPD2BIN[dpd]*4]; \ + if (c!=cstart) {memcpy(c, u+1, 4); c+=3;} \ + else if (*u) {memcpy(c, u+4-*u, 4); c+=*u;} + dpd=(sourhi>>4)&0x3ff; // declet 1 + dpd2char; + dpd=((sourhi&0xf)<<6) | (sourmh>>26); // declet 2 + dpd2char; + dpd=(sourmh>>16)&0x3ff; // declet 3 + dpd2char; + dpd=(sourmh>>6)&0x3ff; // declet 4 + dpd2char; + dpd=((sourmh&0x3f)<<4) | (sourml>>28); // declet 5 + dpd2char; + dpd=(sourml>>18)&0x3ff; // declet 6 + dpd2char; + dpd=(sourml>>8)&0x3ff; // declet 7 + dpd2char; + dpd=((sourml&0xff)<<2) | (sourlo>>30); // declet 8 + dpd2char; + dpd=(sourlo>>20)&0x3ff; // declet 9 + dpd2char; + dpd=(sourlo>>10)&0x3ff; // declet 10 + dpd2char; + dpd=(sourlo)&0x3ff; // declet 11 + dpd2char; + + if (c==cstart) *c++='0'; // all zeros -- make 0 + + if (exp==0) { // integer or NaN case -- easy + *c='\0'; // terminate + return string; + } + + /* non-0 exponent */ + e=0; // assume no E + pre=c-cstart+exp; + // [here, pre-exp is the digits count (==1 for zero)] + if (exp>0 || pre<-5) { // need exponential form + e=pre-1; // calculate E value + pre=1; // assume one digit before '.' + } // exponential form + + /* modify the coefficient, adding 0s, '.', and E+nn as needed */ + s=c-1; // source (LSD) + if (pre>0) { // ddd.ddd (plain), perhaps with E + char *dotat=cstart+pre; + if (dotat=dotat; s--, t--) *t=*s; // open the gap; leave t at gap + *t='.'; // insert the dot + c++; // length increased by one + } + + // finally add the E-part, if needed; it will never be 0, and has + // a maximum length of 4 digits + if (e!=0) { + *c++='E'; // starts with E + *c++='+'; // assume positive + if (e<0) { + *(c-1)='-'; // oops, need '-' + e=-e; // uInt, please + } + if (e<1000) { // 3 (or fewer) digits case + u=&BIN2CHAR[e*4]; // -> length byte + memcpy(c, u+4-*u, 4); // copy fixed 4 characters [is safe] + c+=*u; // bump pointer appropriately + } + else { // 4-digits + Int thou=((e>>3)*1049)>>17; // e/1000 + Int rem=e-(1000*thou); // e%1000 + *c++='0'+(char)thou; + u=&BIN2CHAR[rem*4]; // -> length byte + memcpy(c, u+1, 4); // copy fixed 3+1 characters [is safe] + c+=3; // bump pointer, always 3 digits + } + } + *c='\0'; // add terminator + //printf("res %s\n", string); + return string; + } // pre>0 + + /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */ + t=c+1-pre; + *(t+1)='\0'; // can add terminator now + for (; s>=cstart; s--, t--) *t=*s; // shift whole coefficient right + c=cstart; + *c++='0'; // always starts with 0. + *c++='.'; + for (; pre<0; pre++) *c++='0'; // add any 0's after '.' + //printf("res %s\n", string); + return string; + } // decimal128ToString + +/* ------------------------------------------------------------------ */ +/* to-number -- conversion from numeric string */ +/* */ +/* decimal128FromString(result, string, set); */ +/* */ +/* result is the decimal128 format number which gets the result of */ +/* the conversion */ +/* *string is the character string which should contain a valid */ +/* number (which may be a special value) */ +/* set is the context */ +/* */ +/* The context is supplied to this routine is used for error handling */ +/* (setting of status and traps) and for the rounding mode, only. */ +/* If an error occurs, the result will be a valid decimal128 NaN. */ +/* ------------------------------------------------------------------ */ +decimal128 * decimal128FromString(decimal128 *result, const char *string, + decContext *set) { + decContext dc; // work + decNumber dn; // .. + + decContextDefault(&dc, DEC_INIT_DECIMAL128); // no traps, please + dc.round=set->round; // use supplied rounding + + decNumberFromString(&dn, string, &dc); // will round if needed + decimal128FromNumber(result, &dn, &dc); + if (dc.status!=0) { // something happened + decContextSetStatus(set, dc.status); // .. pass it on + } + return result; + } // decimal128FromString + +/* ------------------------------------------------------------------ */ +/* decimal128IsCanonical -- test whether encoding is canonical */ +/* d128 is the source decimal128 */ +/* returns 1 if the encoding of d128 is canonical, 0 otherwise */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decimal128IsCanonical(const decimal128 *d128) { + decNumber dn; // work + decimal128 canon; // .. + decContext dc; // .. + decContextDefault(&dc, DEC_INIT_DECIMAL128); + decimal128ToNumber(d128, &dn); + decimal128FromNumber(&canon, &dn, &dc);// canon will now be canonical + return memcmp(d128, &canon, DECIMAL128_Bytes)==0; + } // decimal128IsCanonical + +/* ------------------------------------------------------------------ */ +/* decimal128Canonical -- copy an encoding, ensuring it is canonical */ +/* d128 is the source decimal128 */ +/* result is the target (may be the same decimal128) */ +/* returns result */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decimal128 * decimal128Canonical(decimal128 *result, const decimal128 *d128) { + decNumber dn; // work + decContext dc; // .. + decContextDefault(&dc, DEC_INIT_DECIMAL128); + decimal128ToNumber(d128, &dn); + decimal128FromNumber(result, &dn, &dc);// result will now be canonical + return result; + } // decimal128Canonical + +#if DECTRACE || DECCHECK +/* Macros for accessing decimal128 fields. These assume the argument + is a reference (pointer) to the decimal128 structure, and the + decimal128 is in network byte order (big-endian) */ +// Get sign +#define decimal128Sign(d) ((unsigned)(d)->bytes[0]>>7) + +// Get combination field +#define decimal128Comb(d) (((d)->bytes[0] & 0x7c)>>2) + +// Get exponent continuation [does not remove bias] +#define decimal128ExpCon(d) ((((d)->bytes[0] & 0x03)<<10) \ + | ((unsigned)(d)->bytes[1]<<2) \ + | ((unsigned)(d)->bytes[2]>>6)) + +// Set sign [this assumes sign previously 0] +#define decimal128SetSign(d, b) { \ + (d)->bytes[0]|=((unsigned)(b)<<7);} + +// Set exponent continuation [does not apply bias] +// This assumes range has been checked and exponent previously 0; +// type of exponent must be unsigned +#define decimal128SetExpCon(d, e) { \ + (d)->bytes[0]|=(uByte)((e)>>10); \ + (d)->bytes[1] =(uByte)(((e)&0x3fc)>>2); \ + (d)->bytes[2]|=(uByte)(((e)&0x03)<<6);} + +/* ------------------------------------------------------------------ */ +/* decimal128Show -- display a decimal128 in hexadecimal [debug aid] */ +/* d128 -- the number to show */ +/* ------------------------------------------------------------------ */ +// Also shows sign/cob/expconfields extracted +void decimal128Show(const decimal128 *d128) { + char buf[DECIMAL128_Bytes*2+1]; + Int i, j=0; + + if (DECLITEND) { + for (i=0; ibytes[15-i]); + } + printf(" D128> %s [S:%d Cb:%02x Ec:%02x] LittleEndian\n", buf, + d128->bytes[15]>>7, (d128->bytes[15]>>2)&0x1f, + ((d128->bytes[15]&0x3)<<10)|(d128->bytes[14]<<2)| + (d128->bytes[13]>>6)); + } + else { + for (i=0; ibytes[i]); + } + printf(" D128> %s [S:%d Cb:%02x Ec:%02x] BigEndian\n", buf, + decimal128Sign(d128), decimal128Comb(d128), + decimal128ExpCon(d128)); + } + } // decimal128Show +#endif diff --git a/extern/decNumber/decimal128.h b/extern/decNumber/decimal128.h new file mode 100644 index 0000000000..df72acf82b --- /dev/null +++ b/extern/decNumber/decimal128.h @@ -0,0 +1,81 @@ +/* ------------------------------------------------------------------ */ +/* Decimal 128-bit format module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2005. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECIMAL128) + #define DECIMAL128 + #define DEC128NAME "decimal128" /* Short name */ + #define DEC128FULLNAME "Decimal 128-bit Number" /* Verbose name */ + #define DEC128AUTHOR "Mike Cowlishaw" /* Who to blame */ + + /* parameters for decimal128s */ + #define DECIMAL128_Bytes 16 /* length */ + #define DECIMAL128_Pmax 34 /* maximum precision (digits) */ + #define DECIMAL128_Emax 6144 /* maximum adjusted exponent */ + #define DECIMAL128_Emin -6143 /* minimum adjusted exponent */ + #define DECIMAL128_Bias 6176 /* bias for the exponent */ + #define DECIMAL128_String 43 /* maximum string length, +1 */ + #define DECIMAL128_EconL 12 /* exp. continuation length */ + /* highest biased exponent (Elimit-1) */ + #define DECIMAL128_Ehigh (DECIMAL128_Emax+DECIMAL128_Bias-DECIMAL128_Pmax+1) + + /* check enough digits, if pre-defined */ + #if defined(DECNUMDIGITS) + #if (DECNUMDIGITS=34 for safe use + #endif + #endif + + #ifndef DECNUMDIGITS + #define DECNUMDIGITS DECIMAL128_Pmax /* size if not already defined*/ + #endif + #ifndef DECNUMBER + #include "decNumber.h" /* context and number library */ + #endif + + /* Decimal 128-bit type, accessible by bytes */ + typedef struct { + uint8_t bytes[DECIMAL128_Bytes]; /* decimal128: 1, 5, 12, 110 bits*/ + } decimal128; + + /* special values [top byte excluding sign bit; last two bits are */ + /* don't-care for Infinity on input, last bit don't-care for NaN] */ + #if !defined(DECIMAL_NaN) + #define DECIMAL_NaN 0x7c /* 0 11111 00 NaN */ + #define DECIMAL_sNaN 0x7e /* 0 11111 10 sNaN */ + #define DECIMAL_Inf 0x78 /* 0 11110 00 Infinity */ + #endif + + /* ---------------------------------------------------------------- */ + /* Routines */ + /* ---------------------------------------------------------------- */ + /* String conversions */ + decimal128 * decimal128FromString(decimal128 *, const char *, decContext *); + char * decimal128ToString(const decimal128 *, char *); + char * decimal128ToEngString(const decimal128 *, char *); + + /* decNumber conversions */ + decimal128 * decimal128FromNumber(decimal128 *, const decNumber *, + decContext *); + decNumber * decimal128ToNumber(const decimal128 *, decNumber *); + + /* Format-dependent utilities */ + uint32_t decimal128IsCanonical(const decimal128 *); + decimal128 * decimal128Canonical(decimal128 *, const decimal128 *); + +#endif diff --git a/extern/decNumber/decimal32.c b/extern/decNumber/decimal32.c new file mode 100644 index 0000000000..2fad512085 --- /dev/null +++ b/extern/decNumber/decimal32.c @@ -0,0 +1,476 @@ +/* ------------------------------------------------------------------ */ +/* Decimal 32-bit format module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2008. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for decimal32 format numbers. */ +/* Conversions are supplied to and from decNumber and String. */ +/* */ +/* This is used when decNumber provides operations, either for all */ +/* operations or as a proxy between decNumber and decSingle. */ +/* */ +/* Error handling is the same as decNumber (qv.). */ +/* ------------------------------------------------------------------ */ +#include // [for memset/memcpy] +#include // [for printf] + +#define DECNUMDIGITS 7 // make decNumbers with space for 7 +#include "decNumber.h" // base number library +#include "decNumberLocal.h" // decNumber local types, etc. +#include "decimal32.h" // our primary include + +/* Utility tables and routines [in decimal64.c] */ +// DPD2BIN and the reverse are renamed to prevent link-time conflict +// if decQuad is also built in the same executable +#define DPD2BIN DPD2BINx +#define BIN2DPD BIN2DPDx +extern const uInt COMBEXP[32], COMBMSD[32]; +extern const uShort DPD2BIN[1024]; +extern const uShort BIN2DPD[1000]; +extern const uByte BIN2CHAR[4001]; + +extern void decDigitsToDPD(const decNumber *, uInt *, Int); +extern void decDigitsFromDPD(decNumber *, const uInt *, Int); + +#if DECTRACE || DECCHECK +void decimal32Show(const decimal32 *); // for debug +extern void decNumberShow(const decNumber *); // .. +#endif + +/* Useful macro */ +// Clear a structure (e.g., a decNumber) +#define DEC_clear(d) memset(d, 0, sizeof(*d)) + +/* ------------------------------------------------------------------ */ +/* decimal32FromNumber -- convert decNumber to decimal32 */ +/* */ +/* ds is the target decimal32 */ +/* dn is the source number (assumed valid) */ +/* set is the context, used only for reporting errors */ +/* */ +/* The set argument is used only for status reporting and for the */ +/* rounding mode (used if the coefficient is more than DECIMAL32_Pmax */ +/* digits or an overflow is detected). If the exponent is out of the */ +/* valid range then Overflow or Underflow will be raised. */ +/* After Underflow a subnormal result is possible. */ +/* */ +/* DEC_Clamped is set if the number has to be 'folded down' to fit, */ +/* by reducing its exponent and multiplying the coefficient by a */ +/* power of ten, or if the exponent on a zero had to be clamped. */ +/* ------------------------------------------------------------------ */ +decimal32 * decimal32FromNumber(decimal32 *d32, const decNumber *dn, + decContext *set) { + uInt status=0; // status accumulator + Int ae; // adjusted exponent + decNumber dw; // work + decContext dc; // .. + uInt comb, exp; // .. + uInt uiwork; // for macros + uInt targ=0; // target 32-bit + + // If the number has too many digits, or the exponent could be + // out of range then reduce the number under the appropriate + // constraints. This could push the number to Infinity or zero, + // so this check and rounding must be done before generating the + // decimal32] + ae=dn->exponent+dn->digits-1; // [0 if special] + if (dn->digits>DECIMAL32_Pmax // too many digits + || ae>DECIMAL32_Emax // likely overflow + || aeround; // use supplied rounding + decNumberPlus(&dw, dn, &dc); // (round and check) + // [this changes -0 to 0, so enforce the sign...] + dw.bits|=dn->bits&DECNEG; + status=dc.status; // save status + dn=&dw; // use the work number + } // maybe out of range + + if (dn->bits&DECSPECIAL) { // a special value + if (dn->bits&DECINF) targ=DECIMAL_Inf<<24; + else { // sNaN or qNaN + if ((*dn->lsu!=0 || dn->digits>1) // non-zero coefficient + && (dn->digitsbits&DECNAN) targ|=DECIMAL_NaN<<24; + else targ|=DECIMAL_sNaN<<24; + } // a NaN + } // special + + else { // is finite + if (decNumberIsZero(dn)) { // is a zero + // set and clamp exponent + if (dn->exponent<-DECIMAL32_Bias) { + exp=0; // low clamp + status|=DEC_Clamped; + } + else { + exp=dn->exponent+DECIMAL32_Bias; // bias exponent + if (exp>DECIMAL32_Ehigh) { // top clamp + exp=DECIMAL32_Ehigh; + status|=DEC_Clamped; + } + } + comb=(exp>>3) & 0x18; // msd=0, exp top 2 bits .. + } + else { // non-zero finite number + uInt msd; // work + Int pad=0; // coefficient pad digits + + // the dn is known to fit, but it may need to be padded + exp=(uInt)(dn->exponent+DECIMAL32_Bias); // bias exponent + if (exp>DECIMAL32_Ehigh) { // fold-down case + pad=exp-DECIMAL32_Ehigh; + exp=DECIMAL32_Ehigh; // [to maximum] + status|=DEC_Clamped; + } + + // fastpath common case + if (DECDPUN==3 && pad==0) { + targ=BIN2DPD[dn->lsu[0]]; + if (dn->digits>3) targ|=(uInt)(BIN2DPD[dn->lsu[1]])<<10; + msd=(dn->digits==7 ? dn->lsu[2] : 0); + } + else { // general case + decDigitsToDPD(dn, &targ, pad); + // save and clear the top digit + msd=targ>>20; + targ&=0x000fffff; + } + + // create the combination field + if (msd>=8) comb=0x18 | ((exp>>5) & 0x06) | (msd & 0x01); + else comb=((exp>>3) & 0x18) | msd; + } + targ|=comb<<26; // add combination field .. + targ|=(exp&0x3f)<<20; // .. and exponent continuation + } // finite + + if (dn->bits&DECNEG) targ|=0x80000000; // add sign bit + + // now write to storage; this is endian + UBFROMUI(d32->bytes, targ); // directly store the int + + if (status!=0) decContextSetStatus(set, status); // pass on status + // decimal32Show(d32); + return d32; + } // decimal32FromNumber + +/* ------------------------------------------------------------------ */ +/* decimal32ToNumber -- convert decimal32 to decNumber */ +/* d32 is the source decimal32 */ +/* dn is the target number, with appropriate space */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decNumber * decimal32ToNumber(const decimal32 *d32, decNumber *dn) { + uInt msd; // coefficient MSD + uInt exp; // exponent top two bits + uInt comb; // combination field + uInt sour; // source 32-bit + uInt uiwork; // for macros + + // load source from storage; this is endian + sour=UBTOUI(d32->bytes); // directly load the int + + comb=(sour>>26)&0x1f; // combination field + + decNumberZero(dn); // clean number + if (sour&0x80000000) dn->bits=DECNEG; // set sign if negative + + msd=COMBMSD[comb]; // decode the combination field + exp=COMBEXP[comb]; // .. + + if (exp==3) { // is a special + if (msd==0) { + dn->bits|=DECINF; + return dn; // no coefficient needed + } + else if (sour&0x02000000) dn->bits|=DECSNAN; + else dn->bits|=DECNAN; + msd=0; // no top digit + } + else { // is a finite number + dn->exponent=(exp<<6)+((sour>>20)&0x3f)-DECIMAL32_Bias; // unbiased + } + + // get the coefficient + sour&=0x000fffff; // clean coefficient continuation + if (msd) { // non-zero msd + sour|=msd<<20; // prefix to coefficient + decDigitsFromDPD(dn, &sour, 3); // process 3 declets + return dn; + } + // msd=0 + if (!sour) return dn; // easy: coefficient is 0 + if (sour&0x000ffc00) // need 2 declets? + decDigitsFromDPD(dn, &sour, 2); // process 2 declets + else + decDigitsFromDPD(dn, &sour, 1); // process 1 declet + return dn; + } // decimal32ToNumber + +/* ------------------------------------------------------------------ */ +/* to-scientific-string -- conversion to numeric string */ +/* to-engineering-string -- conversion to numeric string */ +/* */ +/* decimal32ToString(d32, string); */ +/* decimal32ToEngString(d32, string); */ +/* */ +/* d32 is the decimal32 format number to convert */ +/* string is the string where the result will be laid out */ +/* */ +/* string must be at least 24 characters */ +/* */ +/* No error is possible, and no status can be set. */ +/* ------------------------------------------------------------------ */ +char * decimal32ToEngString(const decimal32 *d32, char *string){ + decNumber dn; // work + decimal32ToNumber(d32, &dn); + decNumberToEngString(&dn, string); + return string; + } // decimal32ToEngString + +char * decimal32ToString(const decimal32 *d32, char *string){ + uInt msd; // coefficient MSD + Int exp; // exponent top two bits or full + uInt comb; // combination field + char *cstart; // coefficient start + char *c; // output pointer in string + const uByte *u; // work + char *s, *t; // .. (source, target) + Int dpd; // .. + Int pre, e; // .. + uInt uiwork; // for macros + uInt sour; // source 32-bit + + // load source from storage; this is endian + sour=UBTOUI(d32->bytes); // directly load the int + + c=string; // where result will go + if (((Int)sour)<0) *c++='-'; // handle sign + + comb=(sour>>26)&0x1f; // combination field + msd=COMBMSD[comb]; // decode the combination field + exp=COMBEXP[comb]; // .. + + if (exp==3) { + if (msd==0) { // infinity + strcpy(c, "Inf"); + strcpy(c+3, "inity"); + return string; // easy + } + if (sour&0x02000000) *c++='s'; // sNaN + strcpy(c, "NaN"); // complete word + c+=3; // step past + if ((sour&0x000fffff)==0) return string; // zero payload + // otherwise drop through to add integer; set correct exp + exp=0; msd=0; // setup for following code + } + else exp=(exp<<6)+((sour>>20)&0x3f)-DECIMAL32_Bias; // unbiased + + // convert 7 digits of significand to characters + cstart=c; // save start of coefficient + if (msd) *c++='0'+(char)msd; // non-zero most significant digit + + // Now decode the declets. After extracting each one, it is + // decoded to binary and then to a 4-char sequence by table lookup; + // the 4-chars are a 1-char length (significant digits, except 000 + // has length 0). This allows us to left-align the first declet + // with non-zero content, then remaining ones are full 3-char + // length. We use fixed-length memcpys because variable-length + // causes a subroutine call in GCC. (These are length 4 for speed + // and are safe because the array has an extra terminator byte.) + #define dpd2char u=&BIN2CHAR[DPD2BIN[dpd]*4]; \ + if (c!=cstart) {memcpy(c, u+1, 4); c+=3;} \ + else if (*u) {memcpy(c, u+4-*u, 4); c+=*u;} + + dpd=(sour>>10)&0x3ff; // declet 1 + dpd2char; + dpd=(sour)&0x3ff; // declet 2 + dpd2char; + + if (c==cstart) *c++='0'; // all zeros -- make 0 + + if (exp==0) { // integer or NaN case -- easy + *c='\0'; // terminate + return string; + } + + /* non-0 exponent */ + e=0; // assume no E + pre=c-cstart+exp; + // [here, pre-exp is the digits count (==1 for zero)] + if (exp>0 || pre<-5) { // need exponential form + e=pre-1; // calculate E value + pre=1; // assume one digit before '.' + } // exponential form + + /* modify the coefficient, adding 0s, '.', and E+nn as needed */ + s=c-1; // source (LSD) + if (pre>0) { // ddd.ddd (plain), perhaps with E + char *dotat=cstart+pre; + if (dotat=dotat; s--, t--) *t=*s; // open the gap; leave t at gap + *t='.'; // insert the dot + c++; // length increased by one + } + + // finally add the E-part, if needed; it will never be 0, and has + // a maximum length of 3 digits (E-101 case) + if (e!=0) { + *c++='E'; // starts with E + *c++='+'; // assume positive + if (e<0) { + *(c-1)='-'; // oops, need '-' + e=-e; // uInt, please + } + u=&BIN2CHAR[e*4]; // -> length byte + memcpy(c, u+4-*u, 4); // copy fixed 4 characters [is safe] + c+=*u; // bump pointer appropriately + } + *c='\0'; // add terminator + //printf("res %s\n", string); + return string; + } // pre>0 + + /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */ + t=c+1-pre; + *(t+1)='\0'; // can add terminator now + for (; s>=cstart; s--, t--) *t=*s; // shift whole coefficient right + c=cstart; + *c++='0'; // always starts with 0. + *c++='.'; + for (; pre<0; pre++) *c++='0'; // add any 0's after '.' + //printf("res %s\n", string); + return string; + } // decimal32ToString + +/* ------------------------------------------------------------------ */ +/* to-number -- conversion from numeric string */ +/* */ +/* decimal32FromString(result, string, set); */ +/* */ +/* result is the decimal32 format number which gets the result of */ +/* the conversion */ +/* *string is the character string which should contain a valid */ +/* number (which may be a special value) */ +/* set is the context */ +/* */ +/* The context is supplied to this routine is used for error handling */ +/* (setting of status and traps) and for the rounding mode, only. */ +/* If an error occurs, the result will be a valid decimal32 NaN. */ +/* ------------------------------------------------------------------ */ +decimal32 * decimal32FromString(decimal32 *result, const char *string, + decContext *set) { + decContext dc; // work + decNumber dn; // .. + + decContextDefault(&dc, DEC_INIT_DECIMAL32); // no traps, please + dc.round=set->round; // use supplied rounding + + decNumberFromString(&dn, string, &dc); // will round if needed + decimal32FromNumber(result, &dn, &dc); + if (dc.status!=0) { // something happened + decContextSetStatus(set, dc.status); // .. pass it on + } + return result; + } // decimal32FromString + +/* ------------------------------------------------------------------ */ +/* decimal32IsCanonical -- test whether encoding is canonical */ +/* d32 is the source decimal32 */ +/* returns 1 if the encoding of d32 is canonical, 0 otherwise */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decimal32IsCanonical(const decimal32 *d32) { + decNumber dn; // work + decimal32 canon; // .. + decContext dc; // .. + decContextDefault(&dc, DEC_INIT_DECIMAL32); + decimal32ToNumber(d32, &dn); + decimal32FromNumber(&canon, &dn, &dc);// canon will now be canonical + return memcmp(d32, &canon, DECIMAL32_Bytes)==0; + } // decimal32IsCanonical + +/* ------------------------------------------------------------------ */ +/* decimal32Canonical -- copy an encoding, ensuring it is canonical */ +/* d32 is the source decimal32 */ +/* result is the target (may be the same decimal32) */ +/* returns result */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decimal32 * decimal32Canonical(decimal32 *result, const decimal32 *d32) { + decNumber dn; // work + decContext dc; // .. + decContextDefault(&dc, DEC_INIT_DECIMAL32); + decimal32ToNumber(d32, &dn); + decimal32FromNumber(result, &dn, &dc);// result will now be canonical + return result; + } // decimal32Canonical + +#if DECTRACE || DECCHECK +/* Macros for accessing decimal32 fields. These assume the argument + is a reference (pointer) to the decimal32 structure, and the + decimal32 is in network byte order (big-endian) */ +// Get sign +#define decimal32Sign(d) ((unsigned)(d)->bytes[0]>>7) + +// Get combination field +#define decimal32Comb(d) (((d)->bytes[0] & 0x7c)>>2) + +// Get exponent continuation [does not remove bias] +#define decimal32ExpCon(d) ((((d)->bytes[0] & 0x03)<<4) \ + | ((unsigned)(d)->bytes[1]>>4)) + +// Set sign [this assumes sign previously 0] +#define decimal32SetSign(d, b) { \ + (d)->bytes[0]|=((unsigned)(b)<<7);} + +// Set exponent continuation [does not apply bias] +// This assumes range has been checked and exponent previously 0; +// type of exponent must be unsigned +#define decimal32SetExpCon(d, e) { \ + (d)->bytes[0]|=(uByte)((e)>>4); \ + (d)->bytes[1]|=(uByte)(((e)&0x0F)<<4);} + +/* ------------------------------------------------------------------ */ +/* decimal32Show -- display a decimal32 in hexadecimal [debug aid] */ +/* d32 -- the number to show */ +/* ------------------------------------------------------------------ */ +// Also shows sign/cob/expconfields extracted - valid bigendian only +void decimal32Show(const decimal32 *d32) { + char buf[DECIMAL32_Bytes*2+1]; + Int i, j=0; + + if (DECLITEND) { + for (i=0; ibytes[3-i]); + } + printf(" D32> %s [S:%d Cb:%02x Ec:%02x] LittleEndian\n", buf, + d32->bytes[3]>>7, (d32->bytes[3]>>2)&0x1f, + ((d32->bytes[3]&0x3)<<4)| (d32->bytes[2]>>4)); + } + else { + for (i=0; ibytes[i]); + } + printf(" D32> %s [S:%d Cb:%02x Ec:%02x] BigEndian\n", buf, + decimal32Sign(d32), decimal32Comb(d32), decimal32ExpCon(d32)); + } + } // decimal32Show +#endif diff --git a/extern/decNumber/decimal32.h b/extern/decNumber/decimal32.h new file mode 100644 index 0000000000..faaf9a96e2 --- /dev/null +++ b/extern/decNumber/decimal32.h @@ -0,0 +1,81 @@ +/* ------------------------------------------------------------------ */ +/* Decimal 32-bit format module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2006. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECIMAL32) + #define DECIMAL32 + #define DEC32NAME "decimal32" /* Short name */ + #define DEC32FULLNAME "Decimal 32-bit Number" /* Verbose name */ + #define DEC32AUTHOR "Mike Cowlishaw" /* Who to blame */ + + /* parameters for decimal32s */ + #define DECIMAL32_Bytes 4 /* length */ + #define DECIMAL32_Pmax 7 /* maximum precision (digits) */ + #define DECIMAL32_Emax 96 /* maximum adjusted exponent */ + #define DECIMAL32_Emin -95 /* minimum adjusted exponent */ + #define DECIMAL32_Bias 101 /* bias for the exponent */ + #define DECIMAL32_String 15 /* maximum string length, +1 */ + #define DECIMAL32_EconL 6 /* exp. continuation length */ + /* highest biased exponent (Elimit-1) */ + #define DECIMAL32_Ehigh (DECIMAL32_Emax+DECIMAL32_Bias-DECIMAL32_Pmax+1) + + /* check enough digits, if pre-defined */ + #if defined(DECNUMDIGITS) + #if (DECNUMDIGITS=7 for safe use + #endif + #endif + + #ifndef DECNUMDIGITS + #define DECNUMDIGITS DECIMAL32_Pmax /* size if not already defined*/ + #endif + #ifndef DECNUMBER + #include "decNumber.h" /* context and number library */ + #endif + + /* Decimal 32-bit type, accessible by bytes */ + typedef struct { + uint8_t bytes[DECIMAL32_Bytes]; /* decimal32: 1, 5, 6, 20 bits*/ + } decimal32; + + /* special values [top byte excluding sign bit; last two bits are */ + /* don't-care for Infinity on input, last bit don't-care for NaN] */ + #if !defined(DECIMAL_NaN) + #define DECIMAL_NaN 0x7c /* 0 11111 00 NaN */ + #define DECIMAL_sNaN 0x7e /* 0 11111 10 sNaN */ + #define DECIMAL_Inf 0x78 /* 0 11110 00 Infinity */ + #endif + + /* ---------------------------------------------------------------- */ + /* Routines */ + /* ---------------------------------------------------------------- */ + /* String conversions */ + decimal32 * decimal32FromString(decimal32 *, const char *, decContext *); + char * decimal32ToString(const decimal32 *, char *); + char * decimal32ToEngString(const decimal32 *, char *); + + /* decNumber conversions */ + decimal32 * decimal32FromNumber(decimal32 *, const decNumber *, + decContext *); + decNumber * decimal32ToNumber(const decimal32 *, decNumber *); + + /* Format-dependent utilities */ + uint32_t decimal32IsCanonical(const decimal32 *); + decimal32 * decimal32Canonical(decimal32 *, const decimal32 *); + +#endif diff --git a/extern/decNumber/decimal64.c b/extern/decNumber/decimal64.c new file mode 100644 index 0000000000..4bfc5fb6b5 --- /dev/null +++ b/extern/decNumber/decimal64.c @@ -0,0 +1,839 @@ +/* ------------------------------------------------------------------ */ +/* Decimal 64-bit format module */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2009. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ +/* This module comprises the routines for decimal64 format numbers. */ +/* Conversions are supplied to and from decNumber and String. */ +/* */ +/* This is used when decNumber provides operations, either for all */ +/* operations or as a proxy between decNumber and decSingle. */ +/* */ +/* Error handling is the same as decNumber (qv.). */ +/* ------------------------------------------------------------------ */ +#include // [for memset/memcpy] +#include // [for printf] + +#define DECNUMDIGITS 16 // make decNumbers with space for 16 +#include "decNumber.h" // base number library +#include "decNumberLocal.h" // decNumber local types, etc. +#include "decimal64.h" // our primary include + +/* Utility routines and tables [in decimal64.c]; externs for C++ */ +// DPD2BIN and the reverse are renamed to prevent link-time conflict +// if decQuad is also built in the same executable +#define DPD2BIN DPD2BINx +#define BIN2DPD BIN2DPDx +extern const uInt COMBEXP[32], COMBMSD[32]; +extern const uShort DPD2BIN[1024]; +extern const uShort BIN2DPD[1000]; +extern const uByte BIN2CHAR[4001]; + +extern void decDigitsFromDPD(decNumber *, const uInt *, Int); +extern void decDigitsToDPD(const decNumber *, uInt *, Int); + +#if DECTRACE || DECCHECK +void decimal64Show(const decimal64 *); // for debug +extern void decNumberShow(const decNumber *); // .. +#endif + +/* Useful macro */ +// Clear a structure (e.g., a decNumber) +#define DEC_clear(d) memset(d, 0, sizeof(*d)) + +/* define and include the tables to use for conversions */ +#define DEC_BIN2CHAR 1 +#define DEC_DPD2BIN 1 +#define DEC_BIN2DPD 1 // used for all sizes +#include "decDPD.h" // lookup tables + +/* ------------------------------------------------------------------ */ +/* decimal64FromNumber -- convert decNumber to decimal64 */ +/* */ +/* ds is the target decimal64 */ +/* dn is the source number (assumed valid) */ +/* set is the context, used only for reporting errors */ +/* */ +/* The set argument is used only for status reporting and for the */ +/* rounding mode (used if the coefficient is more than DECIMAL64_Pmax */ +/* digits or an overflow is detected). If the exponent is out of the */ +/* valid range then Overflow or Underflow will be raised. */ +/* After Underflow a subnormal result is possible. */ +/* */ +/* DEC_Clamped is set if the number has to be 'folded down' to fit, */ +/* by reducing its exponent and multiplying the coefficient by a */ +/* power of ten, or if the exponent on a zero had to be clamped. */ +/* ------------------------------------------------------------------ */ +decimal64 * decimal64FromNumber(decimal64 *d64, const decNumber *dn, + decContext *set) { + uInt status=0; // status accumulator + Int ae; // adjusted exponent + decNumber dw; // work + decContext dc; // .. + uInt comb, exp; // .. + uInt uiwork; // for macros + uInt targar[2]={0, 0}; // target 64-bit + #define targhi targar[1] // name the word with the sign + #define targlo targar[0] // and the other + + // If the number has too many digits, or the exponent could be + // out of range then reduce the number under the appropriate + // constraints. This could push the number to Infinity or zero, + // so this check and rounding must be done before generating the + // decimal64] + ae=dn->exponent+dn->digits-1; // [0 if special] + if (dn->digits>DECIMAL64_Pmax // too many digits + || ae>DECIMAL64_Emax // likely overflow + || aeround; // use supplied rounding + decNumberPlus(&dw, dn, &dc); // (round and check) + // [this changes -0 to 0, so enforce the sign...] + dw.bits|=dn->bits&DECNEG; + status=dc.status; // save status + dn=&dw; // use the work number + } // maybe out of range + + if (dn->bits&DECSPECIAL) { // a special value + if (dn->bits&DECINF) targhi=DECIMAL_Inf<<24; + else { // sNaN or qNaN + if ((*dn->lsu!=0 || dn->digits>1) // non-zero coefficient + && (dn->digitsbits&DECNAN) targhi|=DECIMAL_NaN<<24; + else targhi|=DECIMAL_sNaN<<24; + } // a NaN + } // special + + else { // is finite + if (decNumberIsZero(dn)) { // is a zero + // set and clamp exponent + if (dn->exponent<-DECIMAL64_Bias) { + exp=0; // low clamp + status|=DEC_Clamped; + } + else { + exp=dn->exponent+DECIMAL64_Bias; // bias exponent + if (exp>DECIMAL64_Ehigh) { // top clamp + exp=DECIMAL64_Ehigh; + status|=DEC_Clamped; + } + } + comb=(exp>>5) & 0x18; // msd=0, exp top 2 bits .. + } + else { // non-zero finite number + uInt msd; // work + Int pad=0; // coefficient pad digits + + // the dn is known to fit, but it may need to be padded + exp=(uInt)(dn->exponent+DECIMAL64_Bias); // bias exponent + if (exp>DECIMAL64_Ehigh) { // fold-down case + pad=exp-DECIMAL64_Ehigh; + exp=DECIMAL64_Ehigh; // [to maximum] + status|=DEC_Clamped; + } + + // fastpath common case + if (DECDPUN==3 && pad==0) { + uInt dpd[6]={0,0,0,0,0,0}; + uInt i; + Int d=dn->digits; + for (i=0; d>0; i++, d-=3) dpd[i]=BIN2DPD[dn->lsu[i]]; + targlo =dpd[0]; + targlo|=dpd[1]<<10; + targlo|=dpd[2]<<20; + if (dn->digits>6) { + targlo|=dpd[3]<<30; + targhi =dpd[3]>>2; + targhi|=dpd[4]<<8; + } + msd=dpd[5]; // [did not really need conversion] + } + else { // general case + decDigitsToDPD(dn, targar, pad); + // save and clear the top digit + msd=targhi>>18; + targhi&=0x0003ffff; + } + + // create the combination field + if (msd>=8) comb=0x18 | ((exp>>7) & 0x06) | (msd & 0x01); + else comb=((exp>>5) & 0x18) | msd; + } + targhi|=comb<<26; // add combination field .. + targhi|=(exp&0xff)<<18; // .. and exponent continuation + } // finite + + if (dn->bits&DECNEG) targhi|=0x80000000; // add sign bit + + // now write to storage; this is now always endian + if (DECLITEND) { + // lo int then hi + UBFROMUI(d64->bytes, targar[0]); + UBFROMUI(d64->bytes+4, targar[1]); + } + else { + // hi int then lo + UBFROMUI(d64->bytes, targar[1]); + UBFROMUI(d64->bytes+4, targar[0]); + } + + if (status!=0) decContextSetStatus(set, status); // pass on status + // decimal64Show(d64); + return d64; + } // decimal64FromNumber + +/* ------------------------------------------------------------------ */ +/* decimal64ToNumber -- convert decimal64 to decNumber */ +/* d64 is the source decimal64 */ +/* dn is the target number, with appropriate space */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decNumber * decimal64ToNumber(const decimal64 *d64, decNumber *dn) { + uInt msd; // coefficient MSD + uInt exp; // exponent top two bits + uInt comb; // combination field + Int need; // work + uInt uiwork; // for macros + uInt sourar[2]; // source 64-bit + #define sourhi sourar[1] // name the word with the sign + #define sourlo sourar[0] // and the lower word + + // load source from storage; this is endian + if (DECLITEND) { + sourlo=UBTOUI(d64->bytes ); // directly load the low int + sourhi=UBTOUI(d64->bytes+4); // then the high int + } + else { + sourhi=UBTOUI(d64->bytes ); // directly load the high int + sourlo=UBTOUI(d64->bytes+4); // then the low int + } + + comb=(sourhi>>26)&0x1f; // combination field + + decNumberZero(dn); // clean number + if (sourhi&0x80000000) dn->bits=DECNEG; // set sign if negative + + msd=COMBMSD[comb]; // decode the combination field + exp=COMBEXP[comb]; // .. + + if (exp==3) { // is a special + if (msd==0) { + dn->bits|=DECINF; + return dn; // no coefficient needed + } + else if (sourhi&0x02000000) dn->bits|=DECSNAN; + else dn->bits|=DECNAN; + msd=0; // no top digit + } + else { // is a finite number + dn->exponent=(exp<<8)+((sourhi>>18)&0xff)-DECIMAL64_Bias; // unbiased + } + + // get the coefficient + sourhi&=0x0003ffff; // clean coefficient continuation + if (msd) { // non-zero msd + sourhi|=msd<<18; // prefix to coefficient + need=6; // process 6 declets + } + else { // msd=0 + if (!sourhi) { // top word 0 + if (!sourlo) return dn; // easy: coefficient is 0 + need=3; // process at least 3 declets + if (sourlo&0xc0000000) need++; // process 4 declets + // [could reduce some more, here] + } + else { // some bits in top word, msd=0 + need=4; // process at least 4 declets + if (sourhi&0x0003ff00) need++; // top declet!=0, process 5 + } + } //msd=0 + + decDigitsFromDPD(dn, sourar, need); // process declets + return dn; + } // decimal64ToNumber + + +/* ------------------------------------------------------------------ */ +/* to-scientific-string -- conversion to numeric string */ +/* to-engineering-string -- conversion to numeric string */ +/* */ +/* decimal64ToString(d64, string); */ +/* decimal64ToEngString(d64, string); */ +/* */ +/* d64 is the decimal64 format number to convert */ +/* string is the string where the result will be laid out */ +/* */ +/* string must be at least 24 characters */ +/* */ +/* No error is possible, and no status can be set. */ +/* ------------------------------------------------------------------ */ +char * decimal64ToEngString(const decimal64 *d64, char *string){ + decNumber dn; // work + decimal64ToNumber(d64, &dn); + decNumberToEngString(&dn, string); + return string; + } // decimal64ToEngString + +char * decimal64ToString(const decimal64 *d64, char *string){ + uInt msd; // coefficient MSD + Int exp; // exponent top two bits or full + uInt comb; // combination field + char *cstart; // coefficient start + char *c; // output pointer in string + const uByte *u; // work + char *s, *t; // .. (source, target) + Int dpd; // .. + Int pre, e; // .. + uInt uiwork; // for macros + + uInt sourar[2]; // source 64-bit + #define sourhi sourar[1] // name the word with the sign + #define sourlo sourar[0] // and the lower word + + // load source from storage; this is endian + if (DECLITEND) { + sourlo=UBTOUI(d64->bytes ); // directly load the low int + sourhi=UBTOUI(d64->bytes+4); // then the high int + } + else { + sourhi=UBTOUI(d64->bytes ); // directly load the high int + sourlo=UBTOUI(d64->bytes+4); // then the low int + } + + c=string; // where result will go + if (((Int)sourhi)<0) *c++='-'; // handle sign + + comb=(sourhi>>26)&0x1f; // combination field + msd=COMBMSD[comb]; // decode the combination field + exp=COMBEXP[comb]; // .. + + if (exp==3) { + if (msd==0) { // infinity + strcpy(c, "Inf"); + strcpy(c+3, "inity"); + return string; // easy + } + if (sourhi&0x02000000) *c++='s'; // sNaN + strcpy(c, "NaN"); // complete word + c+=3; // step past + if (sourlo==0 && (sourhi&0x0003ffff)==0) return string; // zero payload + // otherwise drop through to add integer; set correct exp + exp=0; msd=0; // setup for following code + } + else exp=(exp<<8)+((sourhi>>18)&0xff)-DECIMAL64_Bias; + + // convert 16 digits of significand to characters + cstart=c; // save start of coefficient + if (msd) *c++='0'+(char)msd; // non-zero most significant digit + + // Now decode the declets. After extracting each one, it is + // decoded to binary and then to a 4-char sequence by table lookup; + // the 4-chars are a 1-char length (significant digits, except 000 + // has length 0). This allows us to left-align the first declet + // with non-zero content, then remaining ones are full 3-char + // length. We use fixed-length memcpys because variable-length + // causes a subroutine call in GCC. (These are length 4 for speed + // and are safe because the array has an extra terminator byte.) + #define dpd2char u=&BIN2CHAR[DPD2BIN[dpd]*4]; \ + if (c!=cstart) {memcpy(c, u+1, 4); c+=3;} \ + else if (*u) {memcpy(c, u+4-*u, 4); c+=*u;} + + dpd=(sourhi>>8)&0x3ff; // declet 1 + dpd2char; + dpd=((sourhi&0xff)<<2) | (sourlo>>30); // declet 2 + dpd2char; + dpd=(sourlo>>20)&0x3ff; // declet 3 + dpd2char; + dpd=(sourlo>>10)&0x3ff; // declet 4 + dpd2char; + dpd=(sourlo)&0x3ff; // declet 5 + dpd2char; + + if (c==cstart) *c++='0'; // all zeros -- make 0 + + if (exp==0) { // integer or NaN case -- easy + *c='\0'; // terminate + return string; + } + + /* non-0 exponent */ + e=0; // assume no E + pre=c-cstart+exp; + // [here, pre-exp is the digits count (==1 for zero)] + if (exp>0 || pre<-5) { // need exponential form + e=pre-1; // calculate E value + pre=1; // assume one digit before '.' + } // exponential form + + /* modify the coefficient, adding 0s, '.', and E+nn as needed */ + s=c-1; // source (LSD) + if (pre>0) { // ddd.ddd (plain), perhaps with E + char *dotat=cstart+pre; + if (dotat=dotat; s--, t--) *t=*s; // open the gap; leave t at gap + *t='.'; // insert the dot + c++; // length increased by one + } + + // finally add the E-part, if needed; it will never be 0, and has + // a maximum length of 3 digits + if (e!=0) { + *c++='E'; // starts with E + *c++='+'; // assume positive + if (e<0) { + *(c-1)='-'; // oops, need '-' + e=-e; // uInt, please + } + u=&BIN2CHAR[e*4]; // -> length byte + memcpy(c, u+4-*u, 4); // copy fixed 4 characters [is safe] + c+=*u; // bump pointer appropriately + } + *c='\0'; // add terminator + //printf("res %s\n", string); + return string; + } // pre>0 + + /* -5<=pre<=0: here for plain 0.ddd or 0.000ddd forms (can never have E) */ + t=c+1-pre; + *(t+1)='\0'; // can add terminator now + for (; s>=cstart; s--, t--) *t=*s; // shift whole coefficient right + c=cstart; + *c++='0'; // always starts with 0. + *c++='.'; + for (; pre<0; pre++) *c++='0'; // add any 0's after '.' + //printf("res %s\n", string); + return string; + } // decimal64ToString + +/* ------------------------------------------------------------------ */ +/* to-number -- conversion from numeric string */ +/* */ +/* decimal64FromString(result, string, set); */ +/* */ +/* result is the decimal64 format number which gets the result of */ +/* the conversion */ +/* *string is the character string which should contain a valid */ +/* number (which may be a special value) */ +/* set is the context */ +/* */ +/* The context is supplied to this routine is used for error handling */ +/* (setting of status and traps) and for the rounding mode, only. */ +/* If an error occurs, the result will be a valid decimal64 NaN. */ +/* ------------------------------------------------------------------ */ +decimal64 * decimal64FromString(decimal64 *result, const char *string, + decContext *set) { + decContext dc; // work + decNumber dn; // .. + + decContextDefault(&dc, DEC_INIT_DECIMAL64); // no traps, please + dc.round=set->round; // use supplied rounding + + decNumberFromString(&dn, string, &dc); // will round if needed + + decimal64FromNumber(result, &dn, &dc); + if (dc.status!=0) { // something happened + decContextSetStatus(set, dc.status); // .. pass it on + } + return result; + } // decimal64FromString + +/* ------------------------------------------------------------------ */ +/* decimal64IsCanonical -- test whether encoding is canonical */ +/* d64 is the source decimal64 */ +/* returns 1 if the encoding of d64 is canonical, 0 otherwise */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +uInt decimal64IsCanonical(const decimal64 *d64) { + decNumber dn; // work + decimal64 canon; // .. + decContext dc; // .. + decContextDefault(&dc, DEC_INIT_DECIMAL64); + decimal64ToNumber(d64, &dn); + decimal64FromNumber(&canon, &dn, &dc);// canon will now be canonical + return memcmp(d64, &canon, DECIMAL64_Bytes)==0; + } // decimal64IsCanonical + +/* ------------------------------------------------------------------ */ +/* decimal64Canonical -- copy an encoding, ensuring it is canonical */ +/* d64 is the source decimal64 */ +/* result is the target (may be the same decimal64) */ +/* returns result */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +decimal64 * decimal64Canonical(decimal64 *result, const decimal64 *d64) { + decNumber dn; // work + decContext dc; // .. + decContextDefault(&dc, DEC_INIT_DECIMAL64); + decimal64ToNumber(d64, &dn); + decimal64FromNumber(result, &dn, &dc);// result will now be canonical + return result; + } // decimal64Canonical + +#if DECTRACE || DECCHECK +/* Macros for accessing decimal64 fields. These assume the + argument is a reference (pointer) to the decimal64 structure, + and the decimal64 is in network byte order (big-endian) */ +// Get sign +#define decimal64Sign(d) ((unsigned)(d)->bytes[0]>>7) + +// Get combination field +#define decimal64Comb(d) (((d)->bytes[0] & 0x7c)>>2) + +// Get exponent continuation [does not remove bias] +#define decimal64ExpCon(d) ((((d)->bytes[0] & 0x03)<<6) \ + | ((unsigned)(d)->bytes[1]>>2)) + +// Set sign [this assumes sign previously 0] +#define decimal64SetSign(d, b) { \ + (d)->bytes[0]|=((unsigned)(b)<<7);} + +// Set exponent continuation [does not apply bias] +// This assumes range has been checked and exponent previously 0; +// type of exponent must be unsigned +#define decimal64SetExpCon(d, e) { \ + (d)->bytes[0]|=(uByte)((e)>>6); \ + (d)->bytes[1]|=(uByte)(((e)&0x3F)<<2);} + +/* ------------------------------------------------------------------ */ +/* decimal64Show -- display a decimal64 in hexadecimal [debug aid] */ +/* d64 -- the number to show */ +/* ------------------------------------------------------------------ */ +// Also shows sign/cob/expconfields extracted +void decimal64Show(const decimal64 *d64) { + char buf[DECIMAL64_Bytes*2+1]; + Int i, j=0; + + if (DECLITEND) { + for (i=0; ibytes[7-i]); + } + printf(" D64> %s [S:%d Cb:%02x Ec:%02x] LittleEndian\n", buf, + d64->bytes[7]>>7, (d64->bytes[7]>>2)&0x1f, + ((d64->bytes[7]&0x3)<<6)| (d64->bytes[6]>>2)); + } + else { // big-endian + for (i=0; ibytes[i]); + } + printf(" D64> %s [S:%d Cb:%02x Ec:%02x] BigEndian\n", buf, + decimal64Sign(d64), decimal64Comb(d64), decimal64ExpCon(d64)); + } + } // decimal64Show +#endif + +/* ================================================================== */ +/* Shared utility routines and tables */ +/* ================================================================== */ +// define and include the conversion tables to use for shared code +#if DECDPUN==3 + #define DEC_DPD2BIN 1 +#else + #define DEC_DPD2BCD 1 +#endif +#include "decDPD.h" // lookup tables + +// The maximum number of decNumberUnits needed for a working copy of +// the units array is the ceiling of digits/DECDPUN, where digits is +// the maximum number of digits in any of the formats for which this +// is used. decimal128.h must not be included in this module, so, as +// a very special case, that number is defined as a literal here. +#define DECMAX754 34 +#define DECMAXUNITS ((DECMAX754+DECDPUN-1)/DECDPUN) + +/* ------------------------------------------------------------------ */ +/* Combination field lookup tables (uInts to save measurable work) */ +/* */ +/* COMBEXP - 2-bit most-significant-bits of exponent */ +/* [11 if an Infinity or NaN] */ +/* COMBMSD - 4-bit most-significant-digit */ +/* [0=Infinity, 1=NaN if COMBEXP=11] */ +/* */ +/* Both are indexed by the 5-bit combination field (0-31) */ +/* ------------------------------------------------------------------ */ +const uInt COMBEXP[32]={0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, + 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 1, 1, 2, 2, 3, 3}; +const uInt COMBMSD[32]={0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 0, 1, 2, 3, 4, 5, 6, 7, + 8, 9, 8, 9, 8, 9, 0, 1}; + +/* ------------------------------------------------------------------ */ +/* decDigitsToDPD -- pack coefficient into DPD form */ +/* */ +/* dn is the source number (assumed valid, max DECMAX754 digits) */ +/* targ is 1, 2, or 4-element uInt array, which the caller must */ +/* have cleared to zeros */ +/* shift is the number of 0 digits to add on the right (normally 0) */ +/* */ +/* The coefficient must be known small enough to fit. The full */ +/* coefficient is copied, including the leading 'odd' digit. This */ +/* digit is retrieved and packed into the combination field by the */ +/* caller. */ +/* */ +/* The target uInts are altered only as necessary to receive the */ +/* digits of the decNumber. When more than one uInt is needed, they */ +/* are filled from left to right (that is, the uInt at offset 0 will */ +/* end up with the least-significant digits). */ +/* */ +/* shift is used for 'fold-down' padding. */ +/* */ +/* No error is possible. */ +/* ------------------------------------------------------------------ */ +#if DECDPUN<=4 +// Constant multipliers for divide-by-power-of five using reciprocal +// multiply, after removing powers of 2 by shifting, and final shift +// of 17 [we only need up to **4] +static const uInt multies[]={131073, 26215, 5243, 1049, 210}; +// QUOT10 -- macro to return the quotient of unit u divided by 10**n +#define QUOT10(u, n) ((((uInt)(u)>>(n))*multies[n])>>17) +#endif +void decDigitsToDPD(const decNumber *dn, uInt *targ, Int shift) { + Int cut; // work + Int n; // output bunch counter + Int digits=dn->digits; // digit countdown + uInt dpd; // densely packed decimal value + uInt bin; // binary value 0-999 + uInt *uout=targ; // -> current output uInt + uInt uoff=0; // -> current output offset [from right] + const Unit *inu=dn->lsu; // -> current input unit + Unit uar[DECMAXUNITS]; // working copy of units, iff shifted + #if DECDPUN!=3 // not fast path + Unit in; // current unit + #endif + + if (shift!=0) { // shift towards most significant required + // shift the units array to the left by pad digits and copy + // [this code is a special case of decShiftToMost, which could + // be used instead if exposed and the array were copied first] + const Unit *source; // .. + Unit *target, *first; // .. + uInt next=0; // work + + source=dn->lsu+D2U(digits)-1; // where msu comes from + target=uar+D2U(digits)-1+D2U(shift);// where upper part of first cut goes + cut=DECDPUN-MSUDIGITS(shift); // where to slice + if (cut==0) { // unit-boundary case + for (; source>=dn->lsu; source--, target--) *target=*source; + } + else { + first=uar+D2U(digits+shift)-1; // where msu will end up + for (; source>=dn->lsu; source--, target--) { + // split the source Unit and accumulate remainder for next + #if DECDPUN<=4 + uInt quot=QUOT10(*source, cut); + uInt rem=*source-quot*DECPOWERS[cut]; + next+=quot; + #else + uInt rem=*source%DECPOWERS[cut]; + next+=*source/DECPOWERS[cut]; + #endif + if (target<=first) *target=(Unit)next; // write to target iff valid + next=rem*DECPOWERS[DECDPUN-cut]; // save remainder for next Unit + } + } // shift-move + // propagate remainder to one below and clear the rest + for (; target>=uar; target--) { + *target=(Unit)next; + next=0; + } + digits+=shift; // add count (shift) of zeros added + inu=uar; // use units in working array + } + + /* now densely pack the coefficient into DPD declets */ + + #if DECDPUN!=3 // not fast path + in=*inu; // current unit + cut=0; // at lowest digit + bin=0; // [keep compiler quiet] + #endif + + for(n=0; digits>0; n++) { // each output bunch + #if DECDPUN==3 // fast path, 3-at-a-time + bin=*inu; // 3 digits ready for convert + digits-=3; // [may go negative] + inu++; // may need another + + #else // must collect digit-by-digit + Unit dig; // current digit + Int j; // digit-in-declet count + for (j=0; j<3; j++) { + #if DECDPUN<=4 + Unit temp=(Unit)((uInt)(in*6554)>>16); + dig=(Unit)(in-X10(temp)); + in=temp; + #else + dig=in%10; + in=in/10; + #endif + if (j==0) bin=dig; + else if (j==1) bin+=X10(dig); + else /* j==2 */ bin+=X100(dig); + digits--; + if (digits==0) break; // [also protects *inu below] + cut++; + if (cut==DECDPUN) {inu++; in=*inu; cut=0;} + } + #endif + // here there are 3 digits in bin, or have used all input digits + + dpd=BIN2DPD[bin]; + + // write declet to uInt array + *uout|=dpd<>(10-uoff); // collect top bits + } // n declets + return; + } // decDigitsToDPD + +/* ------------------------------------------------------------------ */ +/* decDigitsFromDPD -- unpack a format's coefficient */ +/* */ +/* dn is the target number, with 7, 16, or 34-digit space. */ +/* sour is a 1, 2, or 4-element uInt array containing only declets */ +/* declets is the number of (right-aligned) declets in sour to */ +/* be processed. This may be 1 more than the obvious number in */ +/* a format, as any top digit is prefixed to the coefficient */ +/* continuation field. It also may be as small as 1, as the */ +/* caller may pre-process leading zero declets. */ +/* */ +/* When doing the 'extra declet' case care is taken to avoid writing */ +/* extra digits when there are leading zeros, as these could overflow */ +/* the units array when DECDPUN is not 3. */ +/* */ +/* The target uInts are used only as necessary to process declets */ +/* declets into the decNumber. When more than one uInt is needed, */ +/* they are used from left to right (that is, the uInt at offset 0 */ +/* provides the least-significant digits). */ +/* */ +/* dn->digits is set, but not the sign or exponent. */ +/* No error is possible [the redundant 888 codes are allowed]. */ +/* ------------------------------------------------------------------ */ +void decDigitsFromDPD(decNumber *dn, const uInt *sour, Int declets) { + + uInt dpd; // collector for 10 bits + Int n; // counter + Unit *uout=dn->lsu; // -> current output unit + Unit *last=uout; // will be unit containing msd + const uInt *uin=sour; // -> current input uInt + uInt uoff=0; // -> current input offset [from right] + + #if DECDPUN!=3 + uInt bcd; // BCD result + uInt nibble; // work + Unit out=0; // accumulator + Int cut=0; // power of ten in current unit + #endif + #if DECDPUN>4 + uInt const *pow; // work + #endif + + // Expand the densely-packed integer, right to left + for (n=declets-1; n>=0; n--) { // count down declets of 10 bits + dpd=*uin>>uoff; + uoff+=10; + if (uoff>32) { // crossed uInt boundary + uin++; + uoff-=32; // [if using this code for wider, check this] + dpd|=*uin<<(10-uoff); // get waiting bits + } + dpd&=0x3ff; // clear uninteresting bits + + #if DECDPUN==3 + if (dpd==0) *uout=0; + else { + *uout=DPD2BIN[dpd]; // convert 10 bits to binary 0-999 + last=uout; // record most significant unit + } + uout++; + } // n + + #else // DECDPUN!=3 + if (dpd==0) { // fastpath [e.g., leading zeros] + // write out three 0 digits (nibbles); out may have digit(s) + cut++; + if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;} + if (n==0) break; // [as below, works even if MSD=0] + cut++; + if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;} + cut++; + if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;} + continue; + } + + bcd=DPD2BCD[dpd]; // convert 10 bits to 12 bits BCD + + // now accumulate the 3 BCD nibbles into units + nibble=bcd & 0x00f; + if (nibble) out=(Unit)(out+nibble*DECPOWERS[cut]); + cut++; + if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;} + bcd>>=4; + + // if this is the last declet and the remaining nibbles in bcd + // are 00 then process no more nibbles, because this could be + // the 'odd' MSD declet and writing any more Units would then + // overflow the unit array + if (n==0 && !bcd) break; + + nibble=bcd & 0x00f; + if (nibble) out=(Unit)(out+nibble*DECPOWERS[cut]); + cut++; + if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;} + bcd>>=4; + + nibble=bcd & 0x00f; + if (nibble) out=(Unit)(out+nibble*DECPOWERS[cut]); + cut++; + if (cut==DECDPUN) {*uout=out; if (out) {last=uout; out=0;} uout++; cut=0;} + } // n + if (cut!=0) { // some more left over + *uout=out; // write out final unit + if (out) last=uout; // and note if non-zero + } + #endif + + // here, last points to the most significant unit with digits; + // inspect it to get the final digits count -- this is essentially + // the same code as decGetDigits in decNumber.c + dn->digits=(last-dn->lsu)*DECDPUN+1; // floor of digits, plus + // must be at least 1 digit + #if DECDPUN>1 + if (*last<10) return; // common odd digit or 0 + dn->digits++; // must be 2 at least + #if DECDPUN>2 + if (*last<100) return; // 10-99 + dn->digits++; // must be 3 at least + #if DECDPUN>3 + if (*last<1000) return; // 100-999 + dn->digits++; // must be 4 at least + #if DECDPUN>4 + for (pow=&DECPOWERS[4]; *last>=*pow; pow++) dn->digits++; + #endif + #endif + #endif + #endif + return; + } //decDigitsFromDPD diff --git a/extern/decNumber/decimal64.h b/extern/decNumber/decimal64.h new file mode 100644 index 0000000000..d2782a34d2 --- /dev/null +++ b/extern/decNumber/decimal64.h @@ -0,0 +1,83 @@ +/* ------------------------------------------------------------------ */ +/* Decimal 64-bit format module header */ +/* ------------------------------------------------------------------ */ +/* Copyright (c) IBM Corporation, 2000, 2005. All rights reserved. */ +/* */ +/* This software is made available under the terms of the */ +/* ICU License -- ICU 1.8.1 and later. */ +/* */ +/* The description and User's Guide ("The decNumber C Library") for */ +/* this software is called decNumber.pdf. This document is */ +/* available, together with arithmetic and format specifications, */ +/* testcases, and Web links, on the General Decimal Arithmetic page. */ +/* */ +/* Please send comments, suggestions, and corrections to the author: */ +/* mfc@uk.ibm.com */ +/* Mike Cowlishaw, IBM Fellow */ +/* IBM UK, PO Box 31, Birmingham Road, Warwick CV34 5JL, UK */ +/* ------------------------------------------------------------------ */ + +#if !defined(DECIMAL64) + #define DECIMAL64 + #define DEC64NAME "decimal64" /* Short name */ + #define DEC64FULLNAME "Decimal 64-bit Number" /* Verbose name */ + #define DEC64AUTHOR "Mike Cowlishaw" /* Who to blame */ + + + /* parameters for decimal64s */ + #define DECIMAL64_Bytes 8 /* length */ + #define DECIMAL64_Pmax 16 /* maximum precision (digits) */ + #define DECIMAL64_Emax 384 /* maximum adjusted exponent */ + #define DECIMAL64_Emin -383 /* minimum adjusted exponent */ + #define DECIMAL64_Bias 398 /* bias for the exponent */ + #define DECIMAL64_String 24 /* maximum string length, +1 */ + #define DECIMAL64_EconL 8 /* exp. continuation length */ + /* highest biased exponent (Elimit-1) */ + #define DECIMAL64_Ehigh (DECIMAL64_Emax+DECIMAL64_Bias-DECIMAL64_Pmax+1) + + /* check enough digits, if pre-defined */ + #if defined(DECNUMDIGITS) + #if (DECNUMDIGITS=16 for safe use + #endif + #endif + + + #ifndef DECNUMDIGITS + #define DECNUMDIGITS DECIMAL64_Pmax /* size if not already defined*/ + #endif + #ifndef DECNUMBER + #include "decNumber.h" /* context and number library */ + #endif + + /* Decimal 64-bit type, accessible by bytes */ + typedef struct { + uint8_t bytes[DECIMAL64_Bytes]; /* decimal64: 1, 5, 8, 50 bits*/ + } decimal64; + + /* special values [top byte excluding sign bit; last two bits are */ + /* don't-care for Infinity on input, last bit don't-care for NaN] */ + #if !defined(DECIMAL_NaN) + #define DECIMAL_NaN 0x7c /* 0 11111 00 NaN */ + #define DECIMAL_sNaN 0x7e /* 0 11111 10 sNaN */ + #define DECIMAL_Inf 0x78 /* 0 11110 00 Infinity */ + #endif + + /* ---------------------------------------------------------------- */ + /* Routines */ + /* ---------------------------------------------------------------- */ + /* String conversions */ + decimal64 * decimal64FromString(decimal64 *, const char *, decContext *); + char * decimal64ToString(const decimal64 *, char *); + char * decimal64ToEngString(const decimal64 *, char *); + + /* decNumber conversions */ + decimal64 * decimal64FromNumber(decimal64 *, const decNumber *, + decContext *); + decNumber * decimal64ToNumber(const decimal64 *, decNumber *); + + /* Format-dependent utilities */ + uint32_t decimal64IsCanonical(const decimal64 *); + decimal64 * decimal64Canonical(decimal64 *, const decimal64 *); + +#endif diff --git a/extern/decNumber/decnumber.pdf b/extern/decNumber/decnumber.pdf new file mode 100644 index 0000000000..f601105beb Binary files /dev/null and b/extern/decNumber/decnumber.pdf differ diff --git a/extern/decNumber/msvc/decNumber_MSVC12.sln b/extern/decNumber/msvc/decNumber_MSVC12.sln new file mode 100644 index 0000000000..e12432872c --- /dev/null +++ b/extern/decNumber/msvc/decNumber_MSVC12.sln @@ -0,0 +1,28 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "decNumber", "decNumber_MSVC12.vcxproj", "{BE4D2DDC-59A0-4CD7-848E-231E13785335}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 + Release|x64 = Release|x64 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Debug|Win32.ActiveCfg = Debug|Win32 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Debug|Win32.Build.0 = Debug|Win32 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Debug|x64.ActiveCfg = Debug|x64 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Debug|x64.Build.0 = Debug|x64 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Release|Win32.ActiveCfg = Release|Win32 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Release|Win32.Build.0 = Release|Win32 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Release|x64.ActiveCfg = Release|x64 + {BE4D2DDC-59A0-4CD7-848E-231E13785335}.Release|x64.Build.0 = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/extern/decNumber/msvc/decNumber_MSVC12.vcxproj b/extern/decNumber/msvc/decNumber_MSVC12.vcxproj new file mode 100644 index 0000000000..b562e03f55 --- /dev/null +++ b/extern/decNumber/msvc/decNumber_MSVC12.vcxproj @@ -0,0 +1,267 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + + true + true + true + true + + + true + true + true + true + + + + + + + + + + + + + + + + + + + + + + + + + + + + + decNumber + {BE4D2DDC-59A0-4CD7-848E-231E13785335} + decNumber + + + + StaticLibrary + false + MultiByte + v120_xp + + + StaticLibrary + false + MultiByte + v120 + + + StaticLibrary + false + MultiByte + v120_xp + + + StaticLibrary + false + MultiByte + v120 + + + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>10.0.40219.1 + ..\lib\$(Platform)\$(Configuration)\ + ..\lib\$(Platform)\$(Configuration)\ + ..\temp\$(Platform)\$(Configuration)\ + ..\temp\$(Platform)\$(Configuration)\ + ..\lib\$(Platform)\$(Configuration)\ + ..\lib\$(Platform)\$(Configuration)\ + ..\temp\$(Platform)\$(Configuration)\ + ..\temp\$(Platform)\$(Configuration)\ + AllRules.ruleset + AllRules.ruleset + + + + + AllRules.ruleset + AllRules.ruleset + + + + + decnumber + decnumber + decnumber + decnumber + + + + Disabled + .;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + EnableFastChecks + MultiThreadedDebugDLL + + + $(IntDir) + $(IntDir) + $(IntDir) + Level3 + true + EditAndContinue + + + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(OutDir)\$(TargetFileName) + true + + + true + .\Debug/libtommath.bsc + + + + + Disabled + .;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebugDLL + + + $(IntDir) + $(IntDir) + $(IntDir) + Level3 + true + ProgramDatabase + + + + + _DEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(OutDir)\$(TargetFileName) + true + + + true + .\Debug/libtommath.bsc + + + + + MaxSpeed + OnlyExplicitInline + .;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + $(IntDir) + $(IntDir) + $(IntDir) + Level3 + true + + + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(OutDir)\$(TargetFileName) + true + + + true + .\Release/libtommath.bsc + + + + + MaxSpeed + OnlyExplicitInline + .;%(AdditionalIncludeDirectories) + _CRT_SECURE_NO_WARNINGS;WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + MultiThreadedDLL + true + + + $(IntDir) + $(IntDir) + $(IntDir) + Level3 + true + + + + + NDEBUG;%(PreprocessorDefinitions) + 0x0409 + + + $(OutDir)\$(TargetFileName) + true + + + true + .\Release/libtommath.bsc + + + + + + \ No newline at end of file diff --git a/extern/decNumber/msvc/decNumber_MSVC12.vcxproj.filters b/extern/decNumber/msvc/decNumber_MSVC12.vcxproj.filters new file mode 100644 index 0000000000..595d745792 --- /dev/null +++ b/extern/decNumber/msvc/decNumber_MSVC12.vcxproj.filters @@ -0,0 +1,84 @@ + + + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + Source files + + + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + Header files + + + + + + + + {638ba91b-1def-4094-a692-752b2ce6f461} + + + {ffbb5d93-e379-40ba-ab58-c62e9e20c810} + + + \ No newline at end of file diff --git a/extern/decNumber/readme.txt b/extern/decNumber/readme.txt new file mode 100644 index 0000000000..1ebc7f397b --- /dev/null +++ b/extern/decNumber/readme.txt @@ -0,0 +1,81 @@ +This is the readme.txt for the decNumber package. It includes +instructions for compiling and testing the package; please read them. +--------------------------------------------------------------------- + +decNumber is distributed in two forms; as a complete package from +the International Components for Unicode (ICU) site (under an as-is +license), or as a collection of Open Source files from the GCC source +repository (under the GPL license). + +If you are using the GCC files, you can obtain the documentation, the +example files mentioned below, and this readme from the General +Decimal Arithmetic web page -- http://speleotrove.com/decimal/ (the +URL for the open source files is also linked from there). + + +The ICU package +--------------- + +The ICU package includes the files: + + * readme.txt (this file) + + * ICU-license.html + + * decNumber.pdf (documentation) + + * The .c and .h file for each module in the package (see the + decNumber documentation), together with other included files. + + * The .c files for each of the examples (example1.c through + example8.c). + +The ICU package is made available under the terms of the ICU License +(ICU 1.8.1 and later) included in the package as ICU-license.html. +Your use of that package indicates your acceptance of the terms and +conditions of that Agreement. + + +To use and check decNumber +-------------------------- + + Please read the appropriate license and documentation before using + this package. If you are upgrading an existing use of decNumber + (with version <= 3.37) please read the Changes Appendix for later + versions -- you may need to change the DECLITEND flag. + + 1. Compile and link example1.c, decNumber.c, and decContext.c + For instance, use: + + gcc -o example1 example1.c decNumber.c decContext.c + + Note: If your compiler does not provide stdint.h or if your C + compiler does not handle line comments (// ...), then see the + User's Guide section in the documentation for further information + (including a sample minimal stdint.h). + + The use of compiler optimization is strongly recommended (e.g., + -O3 for GCC or /O2 for Visual Studio). + + 2. Run example1 with two numeric arguments, for example: + + example1 1.23 1.27 + + this should display: + + 1.23 + 1.27 => 2.50 + + 3. Similarly, try the other examples, at will. + + Examples 2->4 require three files to be compiled, like Example 1. + + Example 5 requires decimal64.c in addition to the core modules. + + Example 6 requires decPacked.c in addition to the core modules. + + Example 7 requires only example7.c decContext.c and decQuad.c + + Example 8 requires example8.c, decContext.c, and decQuad.c, plus + decNumber.c, decimal128.c, and decimal64.c (the latter + for shared tables and code) + diff --git a/lang_helpers/gds_codes.ftn b/lang_helpers/gds_codes.ftn index ca039252b0..e570469fa0 100644 --- a/lang_helpers/gds_codes.ftn +++ b/lang_helpers/gds_codes.ftn @@ -1686,6 +1686,18 @@ C -- PARAMETER (GDS__overriding_user_invalid = 335545136) INTEGER*4 GDS__overriding_system_missing PARAMETER (GDS__overriding_system_missing = 335545137) + INTEGER*4 GDS__decprecision_err + PARAMETER (GDS__decprecision_err = 335545138) + INTEGER*4 GDS__decfloat_divide_by_zero + PARAMETER (GDS__decfloat_divide_by_zero = 335545139) + INTEGER*4 GDS__decfloat_inexact_result + PARAMETER (GDS__decfloat_inexact_result = 335545140) + INTEGER*4 GDS__decfloat_invalid_operation + PARAMETER (GDS__decfloat_invalid_operation = 335545141) + INTEGER*4 GDS__decfloat_overflow + PARAMETER (GDS__decfloat_overflow = 335545142) + INTEGER*4 GDS__decfloat_underflow + PARAMETER (GDS__decfloat_underflow = 335545143) INTEGER*4 GDS__gfix_db_name PARAMETER (GDS__gfix_db_name = 335740929) INTEGER*4 GDS__gfix_invalid_sw diff --git a/lang_helpers/gds_codes.pas b/lang_helpers/gds_codes.pas index 24ffb26fc9..b2a8f54aca 100644 --- a/lang_helpers/gds_codes.pas +++ b/lang_helpers/gds_codes.pas @@ -1681,6 +1681,18 @@ const gds_overriding_user_invalid = 335545136; isc_overriding_system_missing = 335545137; gds_overriding_system_missing = 335545137; + isc_decprecision_err = 335545138; + gds_decprecision_err = 335545138; + isc_decfloat_divide_by_zero = 335545139; + gds_decfloat_divide_by_zero = 335545139; + isc_decfloat_inexact_result = 335545140; + gds_decfloat_inexact_result = 335545140; + isc_decfloat_invalid_operation = 335545141; + gds_decfloat_invalid_operation = 335545141; + isc_decfloat_overflow = 335545142; + gds_decfloat_overflow = 335545142; + isc_decfloat_underflow = 335545143; + gds_decfloat_underflow = 335545143; isc_gfix_db_name = 335740929; gds_gfix_db_name = 335740929; isc_gfix_invalid_sw = 335740930; diff --git a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp index 5b68c6f5dc..b0b01da4fa 100644 --- a/src/auth/SecureRemotePassword/manage/SrpManagement.cpp +++ b/src/auth/SecureRemotePassword/manage/SrpManagement.cpp @@ -233,6 +233,7 @@ public: Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertByte(isc_dpb_sec_attach, TRUE); + dpb.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); unsigned int authBlockSize; const unsigned char* authBlock = logonInfo->authBlock(&authBlockSize); diff --git a/src/auth/SecureRemotePassword/server/SrpServer.cpp b/src/auth/SecureRemotePassword/server/SrpServer.cpp index 34916a3f4a..7a2f53a712 100644 --- a/src/auth/SecureRemotePassword/server/SrpServer.cpp +++ b/src/auth/SecureRemotePassword/server/SrpServer.cpp @@ -141,8 +141,7 @@ int SrpServer::authenticate(CheckStatusWrapper* status, IServerBlock* sb, IWrite ClumpletWriter dpb(ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertByte(isc_dpb_sec_attach, TRUE); dpb.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); - const char* providers = "Providers=" CURRENT_ENGINE; - dpb.insertString(isc_dpb_config, providers, fb_strlen(providers)); + dpb.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); att = p->attachDatabase(status, secDbName, dpb.getBufferLength(), dpb.getBuffer()); check(status); HANDSHAKE_DEBUG(fprintf(stderr, "Srv SRP: attached sec db %s\n", secDbName)); diff --git a/src/auth/SecurityDatabase/LegacyManagement.epp b/src/auth/SecurityDatabase/LegacyManagement.epp index 01ea19b9c5..c9c4b0a9d7 100644 --- a/src/auth/SecurityDatabase/LegacyManagement.epp +++ b/src/auth/SecurityDatabase/LegacyManagement.epp @@ -131,6 +131,7 @@ void SecurityDatabaseManagement::start(Firebird::CheckStatusWrapper* st, Firebir Firebird::ClumpletWriter dpb(Firebird::ClumpletReader::dpbList, MAX_DPB_SIZE); dpb.insertByte(isc_dpb_sec_attach, TRUE); + dpb.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); unsigned int authBlockSize; const unsigned char* authBlock = logonInfo->authBlock(&authBlockSize); @@ -226,6 +227,15 @@ int SecurityDatabaseManagement::release() } #define STR_STORE(to, from) fb_utils::copy_terminate(to, from, sizeof(to)) +#define STR_VSTORE(to, from) string2vary(&to, from, sizeof(to)) +static void string2vary(void* to, Firebird::string& from, size_t to_size) +{ + size_t len = MIN(to_size - sizeof(USHORT), from.size()); + paramvary* v = reinterpret_cast(to); + v->vary_length = len; + memcpy(v->vary_string, from.c_str(), len); +} + int SecurityDatabaseManagement::execute(Firebird::CheckStatusWrapper* st, Firebird::IUser* user, Firebird::IListUsers* callback) @@ -370,7 +380,7 @@ int SecurityDatabaseManagement::execute(Firebird::CheckStatusWrapper* st, Firebi { ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), LEGACY_PASSWORD_SALT); LegacyHash::hash(encrypted2, user->userName()->get(), &encrypted1[2]); - STR_STORE(U.PLG$PASSWD, encrypted2.c_str()); + STR_VSTORE(U.PLG$PASSWD, encrypted2); U.PLG$PASSWD.NULL = ISC_FALSE; } else @@ -455,7 +465,7 @@ int SecurityDatabaseManagement::execute(Firebird::CheckStatusWrapper* st, Firebi { ENC_crypt(encrypted1, sizeof encrypted1, user->password()->get(), LEGACY_PASSWORD_SALT); LegacyHash::hash(encrypted2, user->userName()->get(), &encrypted1[2]); - STR_STORE(U.PLG$PASSWD, encrypted2.c_str()); + STR_VSTORE(U.PLG$PASSWD, encrypted2); U.PLG$PASSWD.NULL = ISC_FALSE; } else if (user->password()->specified()) diff --git a/src/auth/SecurityDatabase/LegacyServer.cpp b/src/auth/SecurityDatabase/LegacyServer.cpp index 1c996f1f92..3f513691ca 100644 --- a/src/auth/SecurityDatabase/LegacyServer.cpp +++ b/src/auth/SecurityDatabase/LegacyServer.cpp @@ -293,8 +293,7 @@ void SecurityDatabase::prepare() dpb.insertString(isc_dpb_trusted_auth, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); // Do not use other providers except current engine - const char* providers = "Providers=" CURRENT_ENGINE; - dpb.insertString(isc_dpb_config, providers, fb_strlen(providers)); + dpb.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); isc_db_handle tempHandle = 0; isc_attach_database(status, 0, secureDbName, &tempHandle, diff --git a/src/burp/backup.epp b/src/burp/backup.epp index cbaa53ad29..7c1df91c46 100644 --- a/src/burp/backup.epp +++ b/src/burp/backup.epp @@ -1618,13 +1618,21 @@ void put_data(burp_rel* relation) add_byte(blr, field->fld_type); break; + case blr_dec64: + case blr_dec128: + alignment = type_alignments[dtype]; + add_byte(blr, field->fld_type); + break; + default: BURP_error_redirect(NULL, 26, SafeArg() << field->fld_type); // msg 26 datatype %ld not understood break; } + if (alignment) offset = FB_ALIGN(offset, alignment); + field->fld_offset = offset; field->fld_parameter = count++; offset += length; diff --git a/src/burp/canonical.cpp b/src/burp/canonical.cpp index 634a9bdf1b..5e92e85825 100644 --- a/src/burp/canonical.cpp +++ b/src/burp/canonical.cpp @@ -136,24 +136,34 @@ ULONG CAN_encode_decode(burp_rel* relation, lstring* buffer, UCHAR* data, bool_t break; case dtype_short: - if (!xdr_short(xdrs, (SSHORT *) p)) + if (!xdr_short(xdrs, (SSHORT*) p)) return FALSE; break; case dtype_long: case dtype_sql_time: case dtype_sql_date: - if (!xdr_long(xdrs, (SLONG *) p)) + if (!xdr_long(xdrs, (SLONG*) p)) return FALSE; break; case dtype_real: - if (!xdr_float(xdrs, (float *) p)) + if (!xdr_float(xdrs, (float*) p)) return FALSE; break; case dtype_double: - if (!xdr_double(xdrs, (double *) p)) + if (!xdr_double(xdrs, (double*) p)) + return FALSE; + break; + + case dtype_dec64: + if (!xdr_dec64(xdrs, (Firebird::Decimal64*) p)) + return FALSE; + break; + + case dtype_dec128: + if (!xdr_dec128(xdrs, (Firebird::Decimal128*) p)) return FALSE; break; diff --git a/src/burp/restore.epp b/src/burp/restore.epp index 77ce97cb05..0ec78f67f3 100644 --- a/src/burp/restore.epp +++ b/src/burp/restore.epp @@ -2805,6 +2805,8 @@ rec_type get_data(BurpGlobals* tdgbl, burp_rel* relation, bool skip_relation) case blr_timestamp: case blr_sql_time: case blr_sql_date: + case blr_dec64: + case blr_dec128: add_byte(blr, field->fld_type); break; @@ -7327,8 +7329,8 @@ bool get_relation(BurpGlobals* tdgbl) * * **************************************/ - SLONG rel_flags = 0, sys_flag = 0, type = 0; - bool rel_flags_null = true, type_null = true; + SLONG rel_flags = 0, sys_flag = fb_sysflag_user, type = rel_persistent; + bool rel_flags_null = true; ISC_QUAD view_blr = isc_blob_null, view_src = isc_blob_null, rel_desc = isc_blob_null, ext_desc = isc_blob_null; bool view_blr_null = true, view_src_null = true, rel_desc_null = true, @@ -7344,6 +7346,24 @@ bool get_relation(BurpGlobals* tdgbl) ext_file_name[0] = '\0'; bool ext_file_name_null = true; + // Before starting to restore relations, commit everything that was restored + // prior to this point. This ensures that no pending error can later affect + // other metadata being restored. + + if (!tdgbl->relations) + { + BURP_verbose (68); + // msg 68 committing meta data + COMMIT + ON_ERROR + general_on_error (); + END_ERROR; + + EXEC SQL SET TRANSACTION NO_AUTO_UNDO; + if (gds_status[1]) + EXEC SQL SET TRANSACTION; + } + // Pick up relation attributes burp_rel* relation = (burp_rel*) BURP_alloc_zero (sizeof(burp_rel)); @@ -7446,10 +7466,7 @@ bool get_relation(BurpGlobals* tdgbl) case att_relation_type: if (tdgbl->RESTORE_format >= 8) - { - type_null = false; type = get_int32(tdgbl); - } else bad_attribute(scan_next_attr, attribute, 111); break; @@ -7488,7 +7505,7 @@ bool get_relation(BurpGlobals* tdgbl) X.RDB$RUNTIME.NULL = TRUE; X.RDB$EXTERNAL_DESCRIPTION.NULL = ext_desc_null; X.RDB$EXTERNAL_FILE.NULL = ext_file_name_null; - X.RDB$RELATION_TYPE.NULL = type_null; + X.RDB$RELATION_TYPE.NULL = FALSE; X.RDB$SQL_SECURITY.NULL = sql_security_null; X.RDB$SYSTEM_FLAG = (USHORT) sys_flag; diff --git a/src/common/CharSet.h b/src/common/CharSet.h index dd6aad6e65..8e61b73aff 100644 --- a/src/common/CharSet.h +++ b/src/common/CharSet.h @@ -31,6 +31,7 @@ #define JRD_CHARSET_H #include "CsConvert.h" +#include "IntlUtil.h" namespace Jrd { @@ -42,8 +43,7 @@ public: public: static void clear(charset* cs) { - if (cs->charset_fn_destroy) - cs->charset_fn_destroy(cs); + Firebird::IntlUtil::finiCharset(cs); delete cs; } }; @@ -116,8 +116,7 @@ public: void destroy() { - if (cs->charset_fn_destroy) - cs->charset_fn_destroy(cs); + Firebird::IntlUtil::finiCharset(cs); } const UCHAR* getSqlMatchAny() const { return sqlMatchAny; } diff --git a/src/common/DecFloat.cpp b/src/common/DecFloat.cpp new file mode 100644 index 0000000000..6960bf636b --- /dev/null +++ b/src/common/DecFloat.cpp @@ -0,0 +1,986 @@ +/* + * PROGRAM: Decimal 64 & 128 type. + * MODULE: DecFloat.cpp + * DESCRIPTION: Floating point with decimal exponent. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2016 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#include "firebird.h" +#include "DecFloat.h" + +#include "StatusArg.h" +#include "gen/iberror.h" + +extern "C" +{ +#include "../../extern/decNumber/decimal128.h" +#include "../../extern/decNumber/decimal64.h" +#include "../../extern/decNumber/decNumber.h" +} + +#include +#include +#include + +using namespace Firebird; + +namespace { + +struct Dec2fb +{ + USHORT decError; + ISC_STATUS fbError; +}; + +Dec2fb dec2fb[] = { + { DEC_IEEE_754_Division_by_zero, isc_decfloat_divide_by_zero }, + { DEC_IEEE_754_Inexact, isc_decfloat_inexact_result }, + { DEC_IEEE_754_Invalid_operation, isc_decfloat_invalid_operation }, + { DEC_IEEE_754_Overflow, isc_decfloat_overflow }, + { DEC_IEEE_754_Underflow, isc_decfloat_underflow }, + { 0, 0 } +}; + +class DecimalContext : public decContext +{ +public: + DecimalContext(const Decimal64*, DecimalStatus ds) + : decSt(ds) + { + init(DEC_INIT_DECIMAL64); + } + + DecimalContext(const Decimal128*, DecimalStatus ds) + : decSt(ds) + { + init(DEC_INIT_DECIMAL128); + } + + ~DecimalContext() NOEXCEPT_ARG(false) + { + // Typically exceptions should better be not thrown from destructors. + // But in our case there should never be any exception raised inside + // Decimal64/128 functions - C library never throw, i.e. dtor will + // be never called due to exception processing. + // Therefore checking status in destructor is safe. + checkForExceptions(); + } + + void checkForExceptions() + { + USHORT unmaskedExceptions = decSt.decExtFlag & decContextGetStatus(this); + if (!unmaskedExceptions) + return; + + decContextZeroStatus(this); + + for (Dec2fb* e = dec2fb; e->decError; ++e) + { + // Arg::Gds(isc_arith_except) as first vector element ? + if (e->decError & unmaskedExceptions) + Arg::Gds(e->fbError).raise(); + } + } + +private: + DecimalStatus decSt; + + void init(int kind) + { + decContextDefault(this, kind); + fb_assert(decSt.roundingMode < USHORT(DEC_ROUND_MAX)); + enum rounding rMode = rounding(decSt.roundingMode); + decContextSetRounding(this, rMode); + traps = 0; // do not raise SIGFPE + } +}; + +CDecimal128 dmax(DBL_MAX, DecimalStatus(0)), dmin(-DBL_MAX, DecimalStatus(0)); +CDecimal128 i64max(MAX_SINT64, DecimalStatus(0)), i64min(MIN_SINT64, DecimalStatus(0)); + +unsigned digits(const unsigned pMax, unsigned char* const coeff, int& exp) +{ + for (unsigned i = 0; i < pMax; ++i) + { + if (coeff[i]) + { + if (i) + { + memmove(coeff, &coeff[i], pMax - i); + memset(&coeff[pMax - i], 0, i); + exp -= i; + } + + i = pMax - i; + + while (!coeff[i - 1]) + { + fb_assert(i > 0); + --i; + } + + return i; + } + } + + return 0; +} + +void make(ULONG* key, + const unsigned pMax, const int bias, const unsigned decSize, + unsigned char* coeff, int sign, int exp) +{ + // normalize coeff & exponent + unsigned dig = digits(pMax, coeff, exp); + + // exponent bias and sign + if (!dig) + { + exp = 0; + sign = 0; + } + else + { + exp += (bias + 2); + if (sign) + exp = -exp; + } + + *key++ = exp; + + // convert to SLONG + fb_assert(pMax / 9 < decSize / sizeof(int)); + memset(key, 0, decSize); + + for (unsigned i = 0; i < pMax; ++i) + { + unsigned c = i / 9; + key[c] *= 10; + key[c] += (sign ? 9 - coeff[i] : coeff[i]); + } +} + +void grab(ULONG* key, + const unsigned pMax, const int bias, const unsigned decSize, + unsigned char* bcd, int& sign, int& exp) +{ + exp = *key++; + sign = 0; + + // parse exp + if (exp < 0) + { + sign = DECFLOAT_Sign; + exp = -exp; + } + + if (exp != 0) + exp -= (bias + 2); + + // convert from SLONG + for (int i = pMax; i--;) + { + int c = i / 9; + bcd[i] = key[c] % 10; + key[c] /= 10; + + if (sign) + bcd[i] = 9 - bcd[i]; + } + + // normalize + for (unsigned i = pMax; i--; ) + { + if (bcd[i]) + { + if (i < pMax - 1) + { + memmove(&bcd[pMax - 1 - i], bcd, i + 1); + memset(bcd, 0, pMax - 1 - i); + exp += (pMax - 1 - i); + } + + break; + } + } +} + +} // anonymous namespace + + + +namespace Firebird { + +void Decimal64::setScale(DecimalStatus decSt, int scale) +{ + if (scale) + { + DecimalContext context(this, decSt); + scale += decDoubleGetExponent(&dec); + decDoubleSetExponent(&dec, &context, scale); + } +} + +#if SIZEOF_LONG < 8 +Decimal64 Decimal64::set(int value, DecimalStatus decSt, int scale) +{ + return set(SLONG(value), decSt, scale); +} +#endif + +Decimal64 Decimal64::set(SLONG value, DecimalStatus decSt, int scale) +{ + decDoubleFromInt32(&dec, value); + setScale(decSt, -scale); + + return *this; +} + +Decimal64 Decimal64::set(SINT64 value, DecimalStatus decSt, int scale) +{ + { + char s[30]; // for sure enough for int64 + sprintf(s, "%" SQUADFORMAT, value); + DecimalContext context(this, decSt); + decDoubleFromString(&dec, s, &context); + } + + setScale(decSt, -scale); + + return *this; +} + +Decimal64 Decimal64::set(const char* value, DecimalStatus decSt) +{ + DecimalContext context(this, decSt); + decDoubleFromString(&dec, value, &context); + + return *this; +} + +Decimal64 Decimal64::set(double value, DecimalStatus decSt) +{ + char s[50]; + sprintf(s, "%.016e", value); + DecimalContext context(this, decSt); + decDoubleFromString(&dec, s, &context); + + return *this; +} + +void Decimal64::toString(DecimalStatus decSt, unsigned length, char* to) const +{ + DecimalContext context(this, decSt); + + if (length) + { + --length; + char s[IDecFloat16::STRING_SIZE]; + memset(s, 0, sizeof(s)); + decDoubleToString(&dec, s); + + if (strlen(s) > length) + decContextSetStatus(&context, DEC_Invalid_operation); + else + length = strlen(s); + + memcpy(to, s, length + 1); + } + else + decContextSetStatus(&context, DEC_Invalid_operation); +} + +void Decimal64::toString(string& to) const +{ + to.grow(IDecFloat16::STRING_SIZE); + toString(DecimalStatus(0), to.length(), to.begin()); // provide long enough string, i.e. no traps + to.recalculate_length(); +} + +UCHAR* Decimal64::getBytes() +{ + return dec.bytes; +} + +Decimal64 Decimal64::abs() const +{ + Decimal64 rc; + decDoubleCopyAbs(&rc.dec, &dec); + return rc; +} + +Decimal64 Decimal64::ceil(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + Decimal64 rc; + decDoubleToIntegralValue(&rc.dec, &dec, &context, DEC_ROUND_CEILING); + return rc; +} + +Decimal64 Decimal64::floor(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + Decimal64 rc; + decDoubleToIntegralValue(&rc.dec, &dec, &context, DEC_ROUND_FLOOR); + return rc; +} + +int Decimal64::compare(DecimalStatus decSt, Decimal64 tgt) const +{ + DecimalContext context(this, decSt); + decDouble r; + decDoubleCompare(&r, &dec, &tgt.dec, &context); + return decDoubleToInt32(&r, &context, DEC_ROUND_HALF_UP); +} + +bool Decimal64::isInf() const +{ + switch (decDoubleClass(&dec)) + { + case DEC_CLASS_NEG_INF: + case DEC_CLASS_POS_INF: + return true; + } + + return false; +} + +bool Decimal64::isNan() const +{ + switch (decDoubleClass(&dec)) + { + case DEC_CLASS_SNAN: + case DEC_CLASS_QNAN: + return true; + } + + return false; +} + +int Decimal64::sign() const +{ + if (decDoubleIsZero(&dec)) + return 0; + if (decDoubleIsSigned(&dec)) + return -1; + return 1; +} + +#ifdef DEV_BUILD +int Decimal64::show() +{ + decDoubleShow(&dec, ""); + return 0; +} +#endif + +Decimal64 Decimal64::neg() const +{ + Decimal64 rc; + decDoubleCopyNegate(&rc.dec, &dec); + return rc; +} + +void Decimal64::makeKey(ULONG* key) const +{ + unsigned char coeff[DECDOUBLE_Pmax]; + int sign = decDoubleGetCoefficient(&dec, coeff); + int exp = decDoubleGetExponent(&dec); + + make(key, DECDOUBLE_Pmax, DECDOUBLE_Bias, sizeof(dec), coeff, sign, exp); +} + +void Decimal64::grabKey(ULONG* key) +{ + int exp, sign; + unsigned char bcd[DECDOUBLE_Pmax]; + + grab(key, DECDOUBLE_Pmax, DECDOUBLE_Bias, sizeof(dec), bcd, sign, exp); + + decDoubleFromBCD(&dec, exp, bcd, sign); +} + +Decimal64 Decimal64::quantize(DecimalStatus decSt, Decimal64 op2) const +{ + DecimalContext context(this, decSt); + Decimal64 rc; + decDoubleQuantize(&rc.dec, &dec, &op2.dec, &context); + return rc; +} + +Decimal64 Decimal64::normalize(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + Decimal64 rc; + decDoubleReduce(&rc.dec, &dec, &context); + return rc; +} + +short Decimal64::totalOrder(Decimal64 op2) const +{ + decDouble r; + decDoubleCompareTotal(&r, &dec, &op2.dec); + fb_assert(!decDoubleIsNaN(&r)); + + DecimalContext context2(this, 0); + return decDoubleToInt32(&r, &context2, DEC_ROUND_HALF_UP); +} + +/* + * decCompare() implements SQL function COMPARE_DECFLOAT() which has non-traditional return values. + * COMPARE_DECFLOAT (X, Y) + * 0 - X == Y + * 1 - X < Y + * 2 - X > Y + * 3 - values unordered + */ + +short Decimal64::decCompare(Decimal64 op2) const +{ + if (decDoubleIsNaN(&dec) || decDoubleIsNaN(&op2.dec)) + return 3; + + switch (totalOrder(op2)) + { + case -1: + return 1; + case 0: + return 0; + case 1: + return 2; + default: + fb_assert(false); + } + + // warning silencer + return 3; +} + +Decimal128 Decimal128::set(Decimal64 d64) +{ + decDoubleToWider(&d64.dec, &dec); + + return *this; +} + +#if SIZEOF_LONG < 8 +Decimal128 Decimal128::set(int value, DecimalStatus decSt, int scale) +{ + return set(SLONG(value), decSt, scale); +} +#endif + +Decimal128 Decimal128::set(SLONG value, DecimalStatus decSt, int scale) +{ + decQuadFromInt32(&dec, value); + setScale(decSt, -scale); + + return *this; +} + +Decimal128 Decimal128::set(SINT64 value, DecimalStatus decSt, int scale) +{ + { + int high = value >> 32; + unsigned low = value & 0xFFFFFFFF; + + DecimalContext context(this, decSt); + decQuad pow2_32; + decQuadFromString(&pow2_32, "4294967296", &context); + + decQuad up, down; + decQuadFromInt32(&up, high); + decQuadFromUInt32(&down, low); + decQuadFMA(&dec, &up, &pow2_32, &down, &context); + } + + setScale(decSt, -scale); + + return *this; +} + +Decimal128 Decimal128::set(const char* value, DecimalStatus decSt) +{ + DecimalContext context(this, decSt); + decQuadFromString(&dec, value, &context); + + return *this; +} + +Decimal128 Decimal128::set(double value, DecimalStatus decSt) +{ + char s[50]; + sprintf(s, "%.016e", value); + DecimalContext context(this, decSt); + decQuadFromString(&dec, s, &context); + + return *this; +} + +Decimal128 Decimal128::operator=(Decimal64 d64) +{ + decDoubleToWider(&d64.dec, &dec); + return *this; +} + +int Decimal128::toInteger(DecimalStatus decSt, int scale) const +{ + Decimal128 tmp(*this); + tmp.setScale(decSt, -scale); + DecimalContext context(this, decSt); + enum rounding rMode = decContextGetRounding(&context); + return decQuadToInt32(&tmp.dec, &context, rMode); +} + +void Decimal128::toString(DecimalStatus decSt, unsigned length, char* to) const +{ + DecimalContext context(this, decSt); + + if (length) + { + --length; + char s[IDecFloat34::STRING_SIZE]; + memset(s, 0, sizeof(s)); + decQuadToString(&dec, s); + + if (strlen(s) > length) + decContextSetStatus(&context, DEC_Invalid_operation); + else + length = strlen(s); + + memcpy(to, s, length + 1); + } + else + decContextSetStatus(&context, DEC_Invalid_operation); +} + +void Decimal128::toString(string& to) const +{ + to.grow(IDecFloat34::STRING_SIZE); + toString(DecimalStatus(0), to.length(), to.begin()); // provide long enough string, i.e. no traps + to.recalculate_length(); +} + +double Decimal128::toDouble(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + + if (compare(decSt, dmin) < 0 || compare(decSt, dmax) > 0) + decContextSetStatus(&context, DEC_Overflow); + else + { + char s[IDecFloat34::STRING_SIZE]; + memset(s, 0, sizeof(s)); + decQuadToString(&dec, s); + return atof(s); + } + + return 0.0; +} + +SINT64 Decimal128::toInt64(DecimalStatus decSt, int scale) const +{ + static CDecimal128 quant(1); + + Decimal128 wrk(*this); + wrk.setScale(decSt, -scale); + wrk = wrk.quantize(decSt, quant); + + if (wrk.compare(decSt, i64min) < 0 || wrk.compare(decSt, i64max) > 0) + { + DecimalContext context(this, decSt); + decContextSetStatus(&context, DEC_Invalid_operation); + return 0; // in case of no trap on invalid operation + } + + unsigned char coeff[DECQUAD_Pmax]; + int sign = decQuadGetCoefficient(&wrk.dec, coeff); + SINT64 rc = 0; + + for (int i = 0; i < DECQUAD_Pmax; ++i) + { + rc *= 10; + if (sign) + rc -= coeff[i]; + else + rc += coeff[i]; + } + + return rc; +} + +UCHAR* Decimal128::getBytes() +{ + return dec.bytes; +} + +Decimal64 Decimal128::toDecimal64(DecimalStatus decSt) const +{ + Decimal64 rc; + DecimalContext context(this, decSt); + decDoubleFromWider(&rc.dec, &dec, &context); + return rc; +} + +void Decimal128::setScale(DecimalStatus decSt, int scale) +{ + if (scale) + { + DecimalContext context(this, decSt); + scale += decQuadGetExponent(&dec); + decQuadSetExponent(&dec, &context, scale); + } +} + +int Decimal128::compare(DecimalStatus decSt, Decimal128 tgt) const +{ + DecimalContext context(this, decSt); + decQuad r; + decQuadCompare(&r, &dec, &tgt.dec, &context); + return decQuadToInt32(&r, &context, DEC_ROUND_HALF_UP); +} + +bool Decimal128::isInf() const +{ + switch(decQuadClass(&dec)) + { + case DEC_CLASS_NEG_INF: + case DEC_CLASS_POS_INF: + return true; + } + + return false; +} + +bool Decimal128::isNan() const +{ + switch(decQuadClass(&dec)) + { + case DEC_CLASS_SNAN: + case DEC_CLASS_QNAN: + return true; + } + + return false; +} + +int Decimal128::sign() const +{ + if (decQuadIsZero(&dec)) + return 0; + if (decQuadIsSigned(&dec)) + return -1; + return 1; +} + +Decimal128 Decimal128::abs() const +{ + Decimal128 rc; + decQuadCopyAbs(&rc.dec, &dec); + return rc; +} + +Decimal128 Decimal128::ceil(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadToIntegralValue(&rc.dec, &dec, &context, DEC_ROUND_CEILING); + return rc; +} + +Decimal128 Decimal128::floor(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadToIntegralValue(&rc.dec, &dec, &context, DEC_ROUND_FLOOR); + return rc; +} + +#ifdef DEV_BUILD +int Decimal128::show() +{ + decQuadShow(&dec, ""); + return 0; +} +#endif + +Decimal128 Decimal128::add(DecimalStatus decSt, Decimal128 op2) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadAdd(&rc.dec, &dec, &op2.dec, &context); + return rc; +} + +Decimal128 Decimal128::sub(DecimalStatus decSt, Decimal128 op2) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadSubtract(&rc.dec, &dec, &op2.dec, &context); + return rc; +} + +Decimal128 Decimal128::mul(DecimalStatus decSt, Decimal128 op2) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadMultiply(&rc.dec, &dec, &op2.dec, &context); + return rc; +} + +Decimal128 Decimal128::div(DecimalStatus decSt, Decimal128 op2) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadDivide(&rc.dec, &dec, &op2.dec, &context); + return rc; +} + +Decimal128 Decimal128::neg() const +{ + Decimal128 rc; + decQuadCopyNegate(&rc.dec, &dec); + return rc; +} + +Decimal128 Decimal128::fma(DecimalStatus decSt, Decimal128 op2, Decimal128 op3) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadFMA(&rc.dec, &op2.dec, &op3.dec, &dec, &context); + return rc; +} + +Decimal128 Decimal128::sqrt(DecimalStatus decSt) const +{ + decNumber dn; + decQuadToNumber(&dec, &dn); + + DecimalContext context(this, decSt); + decNumberSquareRoot(&dn, &dn, &context); + + Decimal128 rc; + decQuadFromNumber(&rc.dec, &dn, &context); + return rc; +} + +Decimal128 Decimal128::pow(DecimalStatus decSt, Decimal128 op2) const +{ + decNumber dn, dn2; + decQuadToNumber(&dec, &dn); + decQuadToNumber(&op2.dec, &dn2); + + DecimalContext context(this, decSt); + decNumberPower(&dn, &dn, &dn2, &context); + + Decimal128 rc; + decQuadFromNumber(&rc.dec, &dn, &context); + return rc; +} + +Decimal128 Decimal128::ln(DecimalStatus decSt) const +{ + decNumber dn; + decQuadToNumber(&dec, &dn); + + DecimalContext context(this, decSt); + decNumberLn(&dn, &dn, &context); + + Decimal128 rc; + decQuadFromNumber(&rc.dec, &dn, &context); + return rc; +} + +Decimal128 Decimal128::log10(DecimalStatus decSt) const +{ + decNumber dn; + decQuadToNumber(&dec, &dn); + + DecimalContext context(this, decSt); + decNumberLog10(&dn, &dn, &context); + + Decimal128 rc; + decQuadFromNumber(&rc.dec, &dn, &context); + return rc; +} + +void Decimal128::makeKey(ULONG* key) const +{ + unsigned char coeff[DECQUAD_Pmax]; + int sign = decQuadGetCoefficient(&dec, coeff); + int exp = decQuadGetExponent(&dec); + + make(key, DECQUAD_Pmax, DECQUAD_Bias, sizeof(dec), coeff, sign, exp); +} + +void Decimal128::grabKey(ULONG* key) +{ + int exp, sign; + unsigned char bcd[DECQUAD_Pmax]; + + grab(key, DECQUAD_Pmax, DECQUAD_Bias, sizeof(dec), bcd, sign, exp); + + decQuadFromBCD(&dec, exp, bcd, sign); +} + +ULONG Decimal128::getIndexKeyLength() +{ + return 17; +} + +ULONG Decimal128::makeIndexKey(vary* buf) +{ + unsigned char coeff[DECQUAD_Pmax + 2]; + int sign = decQuadGetCoefficient(&dec, coeff); + int exp = decQuadGetExponent(&dec); + const int bias = DECQUAD_Bias; + const unsigned pMax = DECQUAD_Pmax; + + // normalize coeff & exponent + unsigned dig = digits(pMax, coeff, exp); + + // exponent bias and sign + exp += (bias + 1); + if (!dig) + exp = 0; + if (sign) + exp = -exp; + exp += 2 * (bias + 1); // make it positive + fb_assert(exp >= 0 && exp < 64 * 1024); + + // encode exp + char* k = buf->vary_string; + *k++ = exp >> 8; + *k++ = exp & 0xff; + + // invert negative + unsigned char* const end = &coeff[dig]; + if (sign && dig) + { + fb_assert(end[-1]); + --end[-1]; + + for (unsigned char* p = coeff; p < end; ++p) + *p = 9 - *p; + } + + // Some 0's in the end - caller, do not forget to reserve additional space on stack + end[0] = end[1] = 0; + + // Avoid bad data in k in case when coeff is zero + *k = 0; + + // Shifts for moving 10-bit values to bytes buffer + struct ShiftTable { UCHAR rshift, lshift; }; + static ShiftTable table[4] = + { + { 2, 6 }, + { 4, 4 }, + { 6, 2 }, + { 8, 0 } + }; + + // compress coeff - 3 decimal digits (999) per 10 bits (1023) + unsigned char* p = coeff; + for (ShiftTable* t = table; p < end; p += 3) + { + USHORT val = p[0] * 100 + p[1] * 10 + p[2]; + fb_assert(val < 1000); // 1024, 10 bit + *k |= (val >> t->rshift); + ++k; + *k = (val << t->lshift); + if (!t->lshift) + { + ++k; + *k = 0; + t = table; + } + else + ++t; + } + if (*k) + ++k; + + // done + buf->vary_length = k - buf->vary_string; + return buf->vary_length; +} + +Decimal128 Decimal128::quantize(DecimalStatus decSt, Decimal128 op2) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadQuantize(&rc.dec, &dec, &op2.dec, &context); + return rc; +} + +Decimal128 Decimal128::normalize(DecimalStatus decSt) const +{ + DecimalContext context(this, decSt); + Decimal128 rc; + decQuadReduce(&rc.dec, &dec, &context); + return rc; +} + +short Decimal128::totalOrder(Decimal128 op2) const +{ + decQuad r; + decQuadCompareTotal(&r, &dec, &op2.dec); + fb_assert(!decQuadIsNaN(&r)); + + DecimalContext context2(this, 0); + return decQuadToInt32(&r, &context2, DEC_ROUND_HALF_UP); +} + +/* + * decCompare() implements SQL function COMPARE_DECFLOAT() which has non-traditional return values. + * COMPARE_DECFLOAT (X, Y) + * 0 - X == Y + * 1 - X < Y + * 2 - X > Y + * 3 - values unordered + */ + +short Decimal128::decCompare(Decimal128 op2) const +{ + if (decQuadIsNaN(&dec) || decQuadIsNaN(&op2.dec)) + return 3; + + switch (totalOrder(op2)) + { + case -1: + return 1; + case 0: + return 0; + case 1: + return 2; + default: + fb_assert(false); + } + + // warning silencer + return 3; +} + +} // namespace Firebird diff --git a/src/common/DecFloat.h b/src/common/DecFloat.h new file mode 100644 index 0000000000..2388ae8661 --- /dev/null +++ b/src/common/DecFloat.h @@ -0,0 +1,195 @@ +/* + * PROGRAM: Decimal 64 & 128 type. + * MODULE: DecFloat.h + * DESCRIPTION: Floating point with decimal exponent. + * + * The contents of this file are subject to the Initial + * Developer's Public License Version 1.0 (the "License"); + * you may not use this file except in compliance with the + * License. You may obtain a copy of the License at + * http://www.ibphoenix.com/main.nfs?a=ibphoenix&page=ibp_idpl. + * + * Software distributed under the License is distributed AS IS, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. + * See the License for the specific language governing rights + * and limitations under the License. + * + * The Original Code was created by Alex Peshkov + * for the Firebird Open Source RDBMS project. + * + * Copyright (c) 2016 Alex Peshkov + * and all contributors signed below. + * + * All Rights Reserved. + * Contributor(s): ______________________________________. + * + * + */ + +#ifndef FB_DECIMAL_FLOAT +#define FB_DECIMAL_FLOAT + +#include "firebird/Interface.h" +#include "fb_exception.h" + +#include "classes/fb_string.h" + +extern "C" +{ +#include "../../extern/decNumber/decQuad.h" +#include "../../extern/decNumber/decDouble.h" +} + +namespace Firebird { + +struct DecimalStatus +{ + DecimalStatus(USHORT exc) + : decExtFlag(exc), roundingMode(DEC_ROUND_HALF_UP) + { } + + USHORT decExtFlag, roundingMode; +}; + +struct DecimalBinding +{ + DecimalBinding() + : bind(DEC_NATIVE), numScale(0) + { } + + enum Bind { DEC_NATIVE, DEC_TEXT, DEC_DOUBLE, DEC_NUMERIC }; + + Bind bind; + SCHAR numScale; +}; + +class Decimal64 +{ + friend class Decimal128; + +public: +#if SIZEOF_LONG < 8 + Decimal64 set(int value, DecimalStatus decSt, int scale); +#endif + Decimal64 set(SLONG value, DecimalStatus decSt, int scale); + Decimal64 set(SINT64 value, DecimalStatus decSt, int scale); + Decimal64 set(const char* value, DecimalStatus decSt); + Decimal64 set(double value, DecimalStatus decSt); + + UCHAR* getBytes(); + Decimal64 abs() const; + Decimal64 ceil(DecimalStatus decSt) const; + Decimal64 floor(DecimalStatus decSt) const; + Decimal64 neg() const; + + void toString(DecimalStatus decSt, unsigned length, char* to) const; + void toString(string& to) const; + + int compare(DecimalStatus decSt, Decimal64 tgt) const; + bool isInf() const; + bool isNan() const; + int sign() const; + + void makeKey(ULONG* key) const; + void grabKey(ULONG* key); + + Decimal64 quantize(DecimalStatus decSt, Decimal64 op2) const; + Decimal64 normalize(DecimalStatus decSt) const; + short totalOrder(Decimal64 op2) const; + short decCompare(Decimal64 op2) const; + +#ifdef DEV_BUILD + int show(); +#endif + +private: + decDouble dec; + + void setScale(DecimalStatus decSt, int scale); +}; + +class Decimal128 +{ + friend class Decimal64; + +public: + Decimal128 set(Decimal64 d64); +#if SIZEOF_LONG < 8 + Decimal128 set(int value, DecimalStatus decSt, int scale); +#endif + Decimal128 set(SLONG value, DecimalStatus decSt, int scale); + Decimal128 set(SINT64 value, DecimalStatus decSt, int scale); + Decimal128 set(const char* value, DecimalStatus decSt); + Decimal128 set(double value, DecimalStatus decSt); + + Decimal128 operator=(Decimal64 d64); + + int toInteger(DecimalStatus decSt, int scale) const; + void toString(DecimalStatus decSt, unsigned length, char* to) const; + void toString(string& to) const; + double toDouble(DecimalStatus decSt) const; + SINT64 toInt64(DecimalStatus decSt, int scale) const; + UCHAR* getBytes(); + Decimal64 toDecimal64(DecimalStatus decSt) const; + Decimal128 abs() const; + Decimal128 ceil(DecimalStatus decSt) const; + Decimal128 floor(DecimalStatus decSt) const; + Decimal128 add(DecimalStatus decSt, Decimal128 op2) const; + Decimal128 sub(DecimalStatus decSt, Decimal128 op2) const; + Decimal128 mul(DecimalStatus decSt, Decimal128 op2) const; + Decimal128 div(DecimalStatus decSt, Decimal128 op2) const; + Decimal128 neg() const; + Decimal128 fma(DecimalStatus decSt, Decimal128 op2, Decimal128 op3) const; + Decimal128 sqrt(DecimalStatus decSt) const; + Decimal128 pow(DecimalStatus decSt, Decimal128 op2) const; + Decimal128 ln(DecimalStatus decSt) const; + Decimal128 log10(DecimalStatus decSt) const; + + int compare(DecimalStatus decSt, Decimal128 tgt) const; + bool isInf() const; + bool isNan() const; + int sign() const; + + void makeKey(ULONG* key) const; + void grabKey(ULONG* key); + static ULONG getIndexKeyLength(); + ULONG makeIndexKey(vary* buf); + + Decimal128 quantize(DecimalStatus decSt, Decimal128 op2) const; + Decimal128 normalize(DecimalStatus decSt) const; + short totalOrder(Decimal128 op2) const; + short decCompare(Decimal128 op2) const; + +#ifdef DEV_BUILD + int show(); +#endif + +private: + decQuad dec; + + void setScale(DecimalStatus decSt, int scale); +}; + +class CDecimal128 : public Decimal128 +{ +public: + CDecimal128(double value, DecimalStatus decSt) + { + set(value, decSt); + } + + CDecimal128(SINT64 value, DecimalStatus decSt) + { + set(value, decSt, 0); + } + + CDecimal128(int value) + { + set(value, DecimalStatus(0), 0); + } +}; + +} // namespace Firebird + + +#endif // FB_DECIMAL_FLOAT diff --git a/src/common/IntlUtil.cpp b/src/common/IntlUtil.cpp index a9eca194c1..ed6d379399 100644 --- a/src/common/IntlUtil.cpp +++ b/src/common/IntlUtil.cpp @@ -49,8 +49,7 @@ namespace ~TextTypeImpl() { - if (cs->charset_fn_destroy) - cs->charset_fn_destroy(cs); + Firebird::IntlUtil::finiCharset(cs); delete cs; delete collation; @@ -539,6 +538,17 @@ bool IntlUtil::initUnicodeCollation(texttype* tt, charset* cs, const ASCII* name } +void IntlUtil::finiCharset(charset* cs) +{ + if (cs->charset_to_unicode.csconvert_fn_destroy) + cs->charset_to_unicode.csconvert_fn_destroy(&cs->charset_to_unicode); + if (cs->charset_from_unicode.csconvert_fn_destroy) + cs->charset_from_unicode.csconvert_fn_destroy(&cs->charset_from_unicode); + if (cs->charset_fn_destroy) + cs->charset_fn_destroy(cs); +} + + ULONG IntlUtil::toLower(Jrd::CharSet* cs, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst, const ULONG* exceptions) { diff --git a/src/common/IntlUtil.h b/src/common/IntlUtil.h index 1d6fc36ec5..03f4008f80 100644 --- a/src/common/IntlUtil.h +++ b/src/common/IntlUtil.h @@ -80,6 +80,8 @@ public: static bool initUnicodeCollation(texttype* tt, charset* cs, const ASCII* name, USHORT attributes, const UCharBuffer& specificAttributes, const string& configInfo); + static void finiCharset(charset* cs); + static ULONG toLower(Jrd::CharSet* cs, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst, const ULONG* exceptions); static ULONG toUpper(Jrd::CharSet* cs, ULONG srcLen, const UCHAR* src, ULONG dstLen, UCHAR* dst, diff --git a/src/common/MsgMetadata.cpp b/src/common/MsgMetadata.cpp index 932e662ade..05d0084c3a 100644 --- a/src/common/MsgMetadata.cpp +++ b/src/common/MsgMetadata.cpp @@ -286,6 +286,7 @@ void MsgMetadata::addItem(const MetaName& name, bool nullable, const dsc& desc) item.subType = sqlSubType; item.length = sqlLen; item.scale = sqlScale; + item.charSet = desc.getCharSet(); item.finished = true; } diff --git a/src/common/StatementMetadata.cpp b/src/common/StatementMetadata.cpp index 71606873bb..ef6f9ee130 100644 --- a/src/common/StatementMetadata.cpp +++ b/src/common/StatementMetadata.cpp @@ -167,7 +167,7 @@ const char* StatementMetadata::getPlan(bool detailed) if (plan->isEmpty()) { - UCHAR info[] = {detailed ? isc_info_sql_explain_plan : isc_info_sql_get_plan}; + UCHAR info[] = {UCHAR(detailed ? isc_info_sql_explain_plan : isc_info_sql_get_plan)}; UCHAR result[INFO_BUFFER_SIZE]; getAndParse(sizeof(info), info, sizeof(result), result); @@ -484,8 +484,8 @@ void StatementMetadata::fetchParameters(UCHAR code, Parameters* parameters) { isc_info_sql_sqlda_start, 2, - (startIndex & 0xFF), - ((startIndex >> 8) & 0xFF), + UCHAR(startIndex & 0xFF), + UCHAR((startIndex >> 8) & 0xFF), code }; memcpy(items + 5, DESCRIBE_VARS, sizeof(DESCRIBE_VARS)); diff --git a/src/common/classes/Hash.cpp b/src/common/classes/Hash.cpp index 6614708889..afe31c9a84 100644 --- a/src/common/classes/Hash.cpp +++ b/src/common/classes/Hash.cpp @@ -43,12 +43,11 @@ namespace { unsigned int hash_value = 0; - UCHAR* p; + UCHAR* const p = (UCHAR*) &hash_value; const UCHAR* q = value; while (length >= 4) { - p = (UCHAR*) &hash_value; p[0] += q[0]; p[1] += q[1]; p[2] += q[2]; @@ -57,18 +56,16 @@ namespace q += 4; } - p = (UCHAR*) &hash_value; - if (length >= 2) { p[0] += q[0]; p[1] += q[1]; length -= 2; + q += 2; } if (length) { - q += 2; *p += *q; } diff --git a/src/common/classes/ImplementHelper.h b/src/common/classes/ImplementHelper.h index c7647fb9b1..cd2b28a128 100644 --- a/src/common/classes/ImplementHelper.h +++ b/src/common/classes/ImplementHelper.h @@ -117,7 +117,7 @@ protected: void refCntDPrt(char f) { if (mark) - fprintf(stderr, "%s %p %c %lld\n", mark, this, f, refCounter.value()); + fprintf(stderr, "%s %p %c %d\n", mark, this, f, int(refCounter.value())); } AtomicCounter refCounter; diff --git a/src/common/classes/InternalMessageBuffer.cpp b/src/common/classes/InternalMessageBuffer.cpp index 9c784529fe..3c57e39a87 100644 --- a/src/common/classes/InternalMessageBuffer.cpp +++ b/src/common/classes/InternalMessageBuffer.cpp @@ -172,6 +172,16 @@ MetadataFromBlr::MetadataFromBlr(unsigned aBlrLength, const unsigned char* aBlr, item->length = sizeof(UCHAR); break; + case blr_dec64: + item->type = SQL_DEC16; + item->length = sizeof(Decimal64); + break; + + case blr_dec128: + item->type = SQL_DEC34; + item->length = sizeof(Decimal128); + break; + default: (Arg::Gds(isc_sqlerr) << Arg::Num(-804) << Arg::Gds(isc_dsql_sqlda_err) diff --git a/src/common/classes/RefCounted.h b/src/common/classes/RefCounted.h index 674e892f45..1766a6190b 100644 --- a/src/common/classes/RefCounted.h +++ b/src/common/classes/RefCounted.h @@ -193,7 +193,7 @@ namespace Firebird return ptr; } - private: + protected: T* assign(T* const p) { if (ptr != p) @@ -215,6 +215,7 @@ namespace Firebird return ptr; } + private: T* ptr; }; diff --git a/src/common/common.h b/src/common/common.h index 0ee7a33842..9f2c8c067f 100644 --- a/src/common/common.h +++ b/src/common/common.h @@ -126,7 +126,7 @@ #define FB_CPU CpuPowerPc #endif -#ifdef i386 +#if defined(i386) || defined(__i386) || defined(__i386__) #define I386 #define FB_CPU CpuIntel #endif /* i386 */ @@ -603,6 +603,13 @@ extern "C" int remove(const char* path); #define FB_EXPORTED #endif +#ifdef HAS_NOEXCEPT +#define NOEXCEPT noexcept +#define NOEXCEPT_ARG(X) noexcept((X)) +#else +#define NOEXCEPT +#define NOEXCEPT_ARG(X) +#endif /* alignment macros */ diff --git a/src/common/cvt.cpp b/src/common/cvt.cpp index 6fffcd3dac..93fa695120 100644 --- a/src/common/cvt.cpp +++ b/src/common/cvt.cpp @@ -137,6 +137,7 @@ enum EXPECT_DATETIME static void datetime_to_text(const dsc*, dsc*, Callbacks*); static void float_to_text(const dsc*, dsc*, Callbacks*); +static void decimal_float_to_text(const dsc*, dsc*, DecimalStatus, Callbacks*); static void integer_to_text(const dsc*, dsc*, Callbacks*); static void string_to_datetime(const dsc*, GDS_TIMESTAMP*, const EXPECT_DATETIME, ErrorFunction); static SINT64 hex_to_value(const char*& string, const char* end); @@ -296,7 +297,38 @@ static void float_to_text(const dsc* from, dsc* to, Callbacks* cb) intermediate.dsc_length = chars_printed - 1; } - CVT_move_common(&intermediate, to, cb); + CVT_move_common(&intermediate, to, 0, cb); +} + + +static void decimal_float_to_text(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* cb) +{ + char temp[50]; + + try + { + Decimal128 d; + if (from->dsc_dtype == dtype_dec64) + d = *((Decimal64*) from->dsc_address); + else + d = *((Decimal128*) from->dsc_address); + + d.toString(decSt, sizeof(temp), temp); + } + catch (const Exception& ex) + { + // reraise using function passed in callbacks + Arg::StatusVector v(ex); + cb->err(v); + } + + dsc intermediate; + intermediate.dsc_dtype = dtype_text; + intermediate.dsc_ttype() = ttype_ascii; + intermediate.dsc_address = reinterpret_cast(temp); + intermediate.dsc_length = strlen(temp); + + CVT_move_common(&intermediate, to, 0, cb); } @@ -343,7 +375,7 @@ static void integer_to_text(const dsc* from, dsc* to, Callbacks* cb) intermediate.dsc_scale = scale; intermediate.dsc_address = (UCHAR*) &n; - CVT_move_common(from, &intermediate, cb); + CVT_move_common(from, &intermediate, 0, cb); // Check for negation, then convert the number to a string of digits @@ -528,7 +560,7 @@ static void string_to_datetime(const dsc* desc, VaryStr<100> buffer; // arbitrarily large const char* p = NULL; - const USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), err); + const USHORT length = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), 0, err); const char* const end = p + length; @@ -862,7 +894,7 @@ static void string_to_datetime(const dsc* desc, } -SLONG CVT_get_long(const dsc* desc, SSHORT scale, ErrorFunction err) +SLONG CVT_get_long(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err) { /************************************** * @@ -878,6 +910,7 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, ErrorFunction err) SLONG value, high; double d, eps; + Decimal128 d128; SINT64 val64; VaryStr<50> buffer; // long enough to represent largest long in ASCII @@ -940,6 +973,15 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, ErrorFunction err) err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); break; + case dtype_dec64: + case dtype_dec128: + if (desc->dsc_dtype == dtype_dec64) + d128 = *((Decimal64*) p); + else + d128 = *((Decimal128*) p); + + return d128.toInteger(decSt, scale); + case dtype_real: case dtype_double: if (desc->dsc_dtype == dtype_real) @@ -986,7 +1028,7 @@ SLONG CVT_get_long(const dsc* desc, SSHORT scale, ErrorFunction err) case dtype_text: { USHORT length = - CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), err); + CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); scale -= CVT_decompose(p, length, dtype_long, &value, err); } break; @@ -1054,7 +1096,7 @@ bool CVT_get_boolean(const dsc* desc, ErrorFunction err) { VaryStr<100> buffer; // arbitrarily large const char* p = NULL; - int len = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), err); + int len = CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), 0, err); // Remove heading and trailing spaces. @@ -1082,7 +1124,7 @@ bool CVT_get_boolean(const dsc* desc, ErrorFunction err) } -double CVT_get_double(const dsc* desc, ErrorFunction err) +double CVT_get_double(const dsc* desc, DecimalStatus decSt, ErrorFunction err, bool* getNumericOverflow) { /************************************** * @@ -1128,6 +1170,18 @@ double CVT_get_double(const dsc* desc, ErrorFunction err) memcpy(&value, desc->dsc_address, sizeof(double)); return value; + case dtype_dec64: + case dtype_dec128: + { + Decimal128 d128; + if (desc->dsc_dtype == dtype_dec64) + d128 = *((Decimal64*) desc->dsc_address); + else + d128 = *((Decimal128*) desc->dsc_address); + + return d128.toDouble(decSt); + } + case dtype_varying: case dtype_cstring: case dtype_text: @@ -1136,7 +1190,7 @@ double CVT_get_double(const dsc* desc, ErrorFunction err) const char* p; const USHORT length = - CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), err); + CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); value = 0.0; int scale = 0; SSHORT sign = 0; @@ -1217,7 +1271,15 @@ double CVT_get_double(const dsc* desc, ErrorFunction err) // later in this routine. if (exp >= SHORT_LIMIT) + { + if (getNumericOverflow) + { + *getNumericOverflow = true; + return 0; + } + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + } } else if (*p == '-' && !digit_seen && !sign) sign = -1; @@ -1250,8 +1312,15 @@ double CVT_get_double(const dsc* desc, ErrorFunction err) // the user know... if (ABSOLUT(scale) > DBL_MAX_10_EXP) - err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + { + if (getNumericOverflow) + { + *getNumericOverflow = true; + return 0; + } + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + } // Repeated division is a good way to mung the least significant bits // of your value, so we have replaced this iterative multiplication/division @@ -1266,7 +1335,15 @@ double CVT_get_double(const dsc* desc, ErrorFunction err) value *= CVT_power_of_ten(-scale); if (isinf(value)) + { + if (getNumericOverflow) + { + *getNumericOverflow = true; + return 0; + } + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); + } } return value; @@ -1308,7 +1385,7 @@ double CVT_get_double(const dsc* desc, ErrorFunction err) } -void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) +void CVT_move_common(const dsc* from, dsc* to, DecimalStatus decSt, Callbacks* cb) { /************************************** * @@ -1391,6 +1468,8 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) case dtype_real: case dtype_double: case dtype_boolean: + case dtype_dec64: + case dtype_dec128: CVT_conversion_error(from, cb->err); break; } @@ -1424,6 +1503,8 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) case dtype_real: case dtype_double: case dtype_boolean: + case dtype_dec64: + case dtype_dec128: CVT_conversion_error(from, cb->err); break; } @@ -1457,6 +1538,8 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) case dtype_real: case dtype_double: case dtype_boolean: + case dtype_dec64: + case dtype_dec128: CVT_conversion_error(from, cb->err); break; } @@ -1548,7 +1631,7 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) { // scope USHORT strtype_unused; UCHAR *ptr; - length = len = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, cb); + length = len = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, decSt, cb); q = ptr; } // end scope @@ -1644,6 +1727,11 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) float_to_text(from, to, cb); return; + case dtype_dec64: + case dtype_dec128: + decimal_float_to_text(from, to, decSt, cb); + return; + case dtype_sql_date: case dtype_sql_time: case dtype_timestamp: @@ -1660,7 +1748,7 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) intermediate.makeText(static_cast(strlen(text)), CS_ASCII, reinterpret_cast(text)); - CVT_move_common(&intermediate, to, cb); + CVT_move_common(&intermediate, to, decSt, cb); return; } @@ -1695,7 +1783,7 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) case dtype_short: { - ULONG lval = CVT_get_long(from, (SSHORT) to->dsc_scale, cb->err); + ULONG lval = CVT_get_long(from, (SSHORT) to->dsc_scale, decSt, cb->err); // TMN: Here we should really have the following fb_assert // fb_assert(lval <= MAX_SSHORT); *(SSHORT*) p = (SSHORT) lval; @@ -1705,11 +1793,11 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) return; case dtype_long: - *(SLONG *) p = CVT_get_long(from, (SSHORT) to->dsc_scale, cb->err); + *(SLONG *) p = CVT_get_long(from, (SSHORT) to->dsc_scale, decSt, cb->err); return; case dtype_int64: - *(SINT64 *) p = CVT_get_int64(from, (SSHORT) to->dsc_scale, cb->err); + *(SINT64 *) p = CVT_get_int64(from, (SSHORT) to->dsc_scale, decSt, cb->err); return; case dtype_quad: @@ -1719,12 +1807,12 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) ((SLONG *) p)[1] = ((SLONG *) q)[1]; return; } - *(SQUAD *) p = CVT_get_quad(from, (SSHORT) to->dsc_scale, cb->err); + *(SQUAD *) p = CVT_get_quad(from, (SSHORT) to->dsc_scale, decSt, cb->err); return; case dtype_real: { - double d_value = CVT_get_double(from, cb->err); + double d_value = CVT_get_double(from, decSt, cb->err); if (ABSOLUT(d_value) > FLOAT_MAX) cb->err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); *(float*) p = (float) d_value; @@ -1736,7 +1824,7 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) { USHORT strtype_unused; UCHAR* ptr; - USHORT len = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, cb); + USHORT len = CVT_get_string_ptr_common(from, &strtype_unused, &ptr, NULL, 0, decSt, cb); if (len == to->dsc_length) { @@ -1751,14 +1839,22 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) case DEFAULT_DOUBLE: #ifdef HPUX { - const double d_value = CVT_get_double(from, cb->err); + const double d_value = CVT_get_double(from, decSt, cb->err); memcpy(p, &d_value, sizeof(double)); } #else - *(double*) p = CVT_get_double(from, cb->err); + *(double*) p = CVT_get_double(from, decSt, cb->err); #endif return; + case dtype_dec64: + *((Decimal64*) p) = CVT_get_dec64(from, decSt, cb->err); + return; + + case dtype_dec128: + *((Decimal128*) p) = CVT_get_dec128(from, decSt, cb->err); + return; + case dtype_boolean: switch (from->dsc_dtype) { @@ -1779,6 +1875,8 @@ void CVT_move_common(const dsc* from, dsc* to, Callbacks* cb) case dtype_quad: case dtype_real: case dtype_double: + case dtype_dec64: + case dtype_dec128: CVT_conversion_error(from, cb->err); break; } @@ -1832,7 +1930,7 @@ void CVT_conversion_error(const dsc* desc, ErrorFunction err) const char* p; VaryStr<128> s; const USHORT length = - CVT_make_string(desc, ttype_ascii, &p, &s, sizeof(s), localError); + CVT_make_string(desc, ttype_ascii, &p, &s, sizeof(s), 0, localError); message.assign(p, length); } /* @@ -1975,7 +2073,49 @@ static void datetime_to_text(const dsc* from, dsc* to, Callbacks* cb) desc.dsc_length = MIN(desc.dsc_length, (to->dsc_length - l)); } - CVT_move_common(&desc, to, cb); + CVT_move_common(&desc, to, 0, cb); +} + + +void CVT_make_null_string(const dsc* desc, + USHORT to_interp, + const char** address, + vary* temp, + USHORT length, + DecimalStatus decSt, + ErrorFunction err) +{ +/************************************** + * + * C V T _ m a k e _ n u l l _ s t r i n g + * + ************************************** + * + * Functional description + * Convert the data from the desc to a zero-terminated string. + * The pointer to this string is returned in address. + * Data always placed to temp buffer. + * + **************************************/ + fb_assert(temp); + + USHORT len = CVT_make_string(desc, to_interp, address, temp, --length, decSt, err); + + if (*address != temp->vary_string) + { + length -= sizeof(USHORT); // Take into an account VaryStr specifics + if (len > length) + { + err(Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) << + Arg::Gds(isc_imp_exc) << + Arg::Gds(isc_trunc_limits) << Arg::Num(length) << Arg::Num(len)); + } + memcpy(temp->vary_string, *address, len); + temp->vary_length = len; + } + + fb_assert(temp->vary_length == len); + temp->vary_string[len] = 0; } @@ -1984,6 +2124,7 @@ USHORT CVT_make_string(const dsc* desc, const char** address, vary* temp, USHORT length, + DecimalStatus decSt, ErrorFunction err) { /************************************** @@ -2035,7 +2176,7 @@ USHORT CVT_make_string(const dsc* desc, temp_desc.dsc_address = (UCHAR *) temp; temp_desc.dsc_dtype = dtype_varying; temp_desc.setTextType(to_interp); - CVT_move(desc, &temp_desc, err); + CVT_move(desc, &temp_desc, decSt, err); *address = temp->vary_string; return temp->vary_length; @@ -2305,7 +2446,7 @@ SSHORT CVT_decompose(const char* string, USHORT CVT_get_string_ptr_common(const dsc* desc, USHORT* ttype, UCHAR** address, - vary* temp, USHORT length, Callbacks* cb) + vary* temp, USHORT length, DecimalStatus decSt, Callbacks* cb) { /************************************** * @@ -2371,7 +2512,7 @@ USHORT CVT_get_string_ptr_common(const dsc* desc, USHORT* ttype, UCHAR** address temp_desc.dsc_address = (UCHAR *) temp; temp_desc.dsc_dtype = dtype_varying; temp_desc.setTextType(ttype_ascii); - CVT_move_common(desc, &temp_desc, cb); + CVT_move_common(desc, &temp_desc, decSt, cb); *address = reinterpret_cast(temp->vary_string); *ttype = INTL_TTYPE(&temp_desc); @@ -2379,7 +2520,182 @@ USHORT CVT_get_string_ptr_common(const dsc* desc, USHORT* ttype, UCHAR** address } -SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, ErrorFunction err) +static inline void SINT64_to_SQUAD(const SINT64 input, const SQUAD& value) +{ + ((SLONG*) &value)[LOW_WORD] = (SLONG) (input & 0xffffffff); + ((SLONG*) &value)[HIGH_WORD] = (SLONG) (input >> 32); +} + + +Decimal64 CVT_get_dec64(const dsc* desc, DecimalStatus decSt, ErrorFunction err) +{ +/************************************** + * + * C V T _ g e t _ d e c 6 4 + * + ************************************** + * + * Functional description + * Convert something arbitrary to a DecFloat(16) / (64 bit). + * + **************************************/ + VaryStr<512> buffer; // long enough to represent largest decimal float in ASCII + Decimal64 d64; + + // adjust exact numeric values to same scaling + int scale = 0; + if (DTYPE_IS_EXACT(desc->dsc_dtype)) + scale = -desc->dsc_scale; + + const char* p = reinterpret_cast(desc->dsc_address); + + try + { + switch (desc->dsc_dtype) + { + case dtype_short: + return d64.set(*(SSHORT*) p, decSt, scale); + + case dtype_long: + return d64.set(*(SLONG*) p, decSt, scale); + + case dtype_quad: + return d64.set(CVT_get_int64(desc, 0, decSt, err), decSt, scale); + + case dtype_int64: + return d64.set(*(SINT64*) p, decSt, scale); + + case dtype_varying: + case dtype_cstring: + case dtype_text: + CVT_make_null_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer) - 1, decSt, err); + return d64.set(buffer.vary_string, decSt); + + case dtype_blob: + case dtype_sql_date: + case dtype_sql_time: + case dtype_timestamp: + case dtype_array: + case dtype_dbkey: + case dtype_boolean: + CVT_conversion_error(desc, err); + break; + + case dtype_real: + return d64.set(*((float*) p), decSt); + + case dtype_double: + return d64.set(*((double*) p), decSt); + + case dtype_dec64: + return *(Decimal64*) p; + + case dtype_dec128: + return ((Decimal128*) p)->toDecimal64(decSt); + + default: + fb_assert(false); + err(Arg::Gds(isc_badblk)); // internal error + break; + } + } + catch (const Exception& ex) + { + // reraise using passed error function + Arg::StatusVector v(ex); + err(v); + } + + // compiler silencer + return d64; +} + + +Decimal128 CVT_get_dec128(const dsc* desc, DecimalStatus decSt, ErrorFunction err) +{ +/************************************** + * + * C V T _ g e t _ d e c 1 2 8 + * + ************************************** + * + * Functional description + * Convert something arbitrary to a DecFloat(34) / (128 bit). + * + **************************************/ + VaryStr<1024> buffer; // represents unreasonably long decfloat literal in ASCII + Decimal128 d128; + + // adjust exact numeric values to same scaling + int scale = 0; + if (DTYPE_IS_EXACT(desc->dsc_dtype)) + scale = -desc->dsc_scale; + + const char* p = reinterpret_cast(desc->dsc_address); + + try + { + switch (desc->dsc_dtype) + { + case dtype_short: + return d128.set(*(SSHORT*) p, decSt, scale); + + case dtype_long: + return d128.set(*(SLONG*) p, decSt, scale); + + case dtype_quad: + return d128.set(CVT_get_int64(desc, 0, decSt, err), decSt, scale); + + case dtype_int64: + return d128.set(*(SINT64*) p, decSt, scale); + + case dtype_varying: + case dtype_cstring: + case dtype_text: + CVT_make_null_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer) - 1, decSt, err); + return d128.set(buffer.vary_string, decSt); + + case dtype_blob: + case dtype_sql_date: + case dtype_sql_time: + case dtype_timestamp: + case dtype_array: + case dtype_dbkey: + case dtype_boolean: + CVT_conversion_error(desc, err); + break; + + case dtype_real: + return d128.set(*((float*) p), decSt); + + case dtype_double: + return d128.set(*((double*) p), decSt); + + case dtype_dec64: + return (d128 = (*(Decimal64*) p)); // cast to higher precision never cause rounding/traps + + case dtype_dec128: + return *(Decimal128*) p; + + default: + fb_assert(false); + err(Arg::Gds(isc_badblk)); // internal error + break; + } + } + catch (const Exception& ex) + { + // reraise using passed error function + Arg::StatusVector v(ex); + err(v); + } + + // compiler silencer + return d128; +} + + +SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err) { /************************************** * @@ -2425,11 +2741,7 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, ErrorFunction err) break; case dtype_int64: - { - const SINT64 input = *(SINT64*) p; - ((SLONG*) &value)[LOW_WORD] = (SLONG) (input & 0xffffffff); - ((SLONG*) &value)[HIGH_WORD] = (SLONG) (input >> 32); - } + SINT64_to_SQUAD(*(SINT64*) p, value); break; case dtype_varying: @@ -2437,7 +2749,7 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, ErrorFunction err) case dtype_text: { USHORT length = - CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), err); + CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); scale -= CVT_decompose(p, length, dtype_quad, &value.gds_quad_high, err); } break; @@ -2452,6 +2764,11 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, ErrorFunction err) CVT_conversion_error(desc, err); break; + case dtype_dec64: + case dtype_dec128: + SINT64_to_SQUAD(CVT_get_int64(desc, scale, decSt, err), value); + break; + default: fb_assert(false); err(Arg::Gds(isc_badblk)); // internal error @@ -2470,7 +2787,7 @@ SQUAD CVT_get_quad(const dsc* desc, SSHORT scale, ErrorFunction err) } -SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, ErrorFunction err) +SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, DecimalStatus decSt, ErrorFunction err) { /************************************** * @@ -2512,6 +2829,18 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, ErrorFunction err) value = (((SINT64) ((SLONG*) p)[HIGH_WORD]) << 32) + (((ULONG*) p)[LOW_WORD]); break; + case dtype_dec64: + case dtype_dec128: + { + Decimal128 d128; + if (desc->dsc_dtype == dtype_dec64) + d128 = *((Decimal64*) p); + else + d128 = *((Decimal128*) p); + + return d128.toInt64(decSt, scale); + } + case dtype_real: case dtype_double: if (desc->dsc_dtype == dtype_real) @@ -2556,7 +2885,7 @@ SINT64 CVT_get_int64(const dsc* desc, SSHORT scale, ErrorFunction err) case dtype_text: { USHORT length = - CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), err); + CVT_make_string(desc, ttype_ascii, &p, &buffer, sizeof(buffer), decSt, err); scale -= CVT_decompose(p, length, dtype_int64, (SLONG *) & value, err); } break; @@ -2725,7 +3054,7 @@ namespace USHORT CVT_get_string_ptr(const dsc* desc, USHORT* ttype, UCHAR** address, - vary* temp, USHORT length, ErrorFunction err) + vary* temp, USHORT length, DecimalStatus decSt, ErrorFunction err) { /************************************** * @@ -2752,11 +3081,11 @@ USHORT CVT_get_string_ptr(const dsc* desc, USHORT* ttype, UCHAR** address, fb_assert(err != NULL); CommonCallbacks callbacks(err); - return CVT_get_string_ptr_common(desc, ttype, address, temp, length, &callbacks); + return CVT_get_string_ptr_common(desc, ttype, address, temp, length, decSt, &callbacks); } -void CVT_move(const dsc* from, dsc* to, ErrorFunction err) +void CVT_move(const dsc* from, dsc* to, DecimalStatus decSt, ErrorFunction err) { /************************************** * @@ -2769,5 +3098,5 @@ void CVT_move(const dsc* from, dsc* to, ErrorFunction err) * **************************************/ CommonCallbacks callbacks(err); - CVT_move_common(from, to, &callbacks); + CVT_move_common(from, to, decSt, &callbacks); } diff --git a/src/common/cvt.h b/src/common/cvt.h index f0b72bb85e..ca846413d7 100644 --- a/src/common/cvt.h +++ b/src/common/cvt.h @@ -29,6 +29,8 @@ #ifndef COMMON_CVT_H #define COMMON_CVT_H +#include "../common/DecFloat.h" + namespace Jrd { class CharSet; @@ -70,16 +72,19 @@ public: void CVT_conversion_error(const dsc*, ErrorFunction); double CVT_power_of_ten(const int); -SLONG CVT_get_long(const dsc*, SSHORT, ErrorFunction); +SLONG CVT_get_long(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); bool CVT_get_boolean(const dsc*, ErrorFunction); -double CVT_get_double(const dsc*, ErrorFunction); -USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, ErrorFunction); -void CVT_move_common(const dsc*, dsc*, Firebird::Callbacks*); -void CVT_move(const dsc*, dsc*, ErrorFunction); +double CVT_get_double(const dsc*, Firebird::DecimalStatus, ErrorFunction, bool* getNumericOverflow = nullptr); +Firebird::Decimal64 CVT_get_dec64(const dsc*, Firebird::DecimalStatus, ErrorFunction); +Firebird::Decimal128 CVT_get_dec128(const dsc*, Firebird::DecimalStatus, ErrorFunction); +USHORT CVT_make_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); +void CVT_make_null_string(const dsc*, USHORT, const char**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); +void CVT_move_common(const dsc*, dsc*, Firebird::DecimalStatus, Firebird::Callbacks*); +void CVT_move(const dsc*, dsc*, Firebird::DecimalStatus, ErrorFunction); SSHORT CVT_decompose(const char*, USHORT, SSHORT, SLONG*, ErrorFunction); -USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, ErrorFunction); -USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::Callbacks*); -SINT64 CVT_get_int64(const dsc*, SSHORT, ErrorFunction); -SQUAD CVT_get_quad(const dsc*, SSHORT, ErrorFunction); +USHORT CVT_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, ErrorFunction); +USHORT CVT_get_string_ptr_common(const dsc*, USHORT*, UCHAR**, vary*, USHORT, Firebird::DecimalStatus, Firebird::Callbacks*); +SINT64 CVT_get_int64(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); +SQUAD CVT_get_quad(const dsc*, SSHORT, Firebird::DecimalStatus, ErrorFunction); #endif //COMMON_CVT_H diff --git a/src/common/dsc.cpp b/src/common/dsc.cpp index cb1a365cba..d4b7da2157 100644 --- a/src/common/dsc.cpp +++ b/src/common/dsc.cpp @@ -64,7 +64,9 @@ static const USHORT _DSC_convert_to_text_length[DTYPE_TYPE_MAX] = 9, // dtype_array FFFF:FFFF 20, // dtype_int64 -9223372036854775808 0, // dtype_dbkey - 5 // dtype_boolean + 5, // dtype_boolean + 23, // dtype_dec64 1 + 1 + 1 + 1 + 16(34) + 3(4) + 42 // dtype_dec128 +- . e +- coeff + exp }; // blr to dsc type conversions @@ -89,7 +91,9 @@ static const USHORT DSC_blr_type_mapping[] = blr_blob, blr_int64, blr_null, - blr_bool + blr_bool, + blr_dec64, + blr_dec128 }; // Unimplemented names are in lowercase & @@ -117,7 +121,9 @@ static const TEXT* const DSC_dtype_names[] = "ARRAY", "BIGINT", "DB_KEY", - "BOOLEAN" + "BOOLEAN", + "DECFLOAT(16)", + "DECFLOAT(34)" }; @@ -130,12 +136,12 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = { /* - dtype_unknown dtype_text dtype_cstring dtype_varying - 4 (unused) 5 (unused) dtype_packed dtype_byte - dtype_short dtype_long dtype_quad dtype_real + dtype_unknown dtype_text dtype_cstring dtype_varying + 4 (unused) 5 (unused) dtype_packed dtype_byte + dtype_short dtype_long dtype_quad dtype_real dtype_double dtype_d_float dtype_sql_date dtype_sql_time - dtype_timestamp dtype_blob dtype_array dtype_int64 - dtype_dbkey, dtype_boolean + dtype_timestamp dtype_blob dtype_array dtype_int64 + dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 */ // dtype_unknown @@ -144,7 +150,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_unknown, dtype_unknown, DTYPE_CANNOT, dtype_unknown, dtype_unknown, dtype_unknown, dtype_unknown, dtype_unknown, dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, dtype_unknown}, // dtype_text {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -152,7 +158,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -160,7 +166,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -168,7 +174,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -176,7 +182,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // 5 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -184,7 +190,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_packed {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -192,7 +198,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_byte {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -200,7 +206,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_short {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -208,7 +214,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -216,7 +222,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -224,7 +230,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_real {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -232,7 +238,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_double {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -240,7 +246,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_d_float -- VMS deprecated {dtype_unknown, dtype_d_float, dtype_d_float, dtype_d_float, @@ -248,7 +254,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_d_float, dtype_d_float, DTYPE_CANNOT, dtype_d_float, dtype_d_float, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, dtype_d_float}, // dtype_sql_date {dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -256,7 +262,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_sql_date, dtype_sql_date, DTYPE_CANNOT, dtype_sql_date, dtype_sql_date, dtype_sql_date, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_date, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_date, dtype_sql_date}, // dtype_sql_time {dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -264,7 +270,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_sql_time, dtype_sql_time, DTYPE_CANNOT, dtype_sql_time, dtype_sql_time, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_time, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_time, dtype_sql_time}, // dtype_timestamp {dtype_unknown, dtype_timestamp, dtype_timestamp, dtype_timestamp, @@ -272,7 +278,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_timestamp, dtype_timestamp, DTYPE_CANNOT, dtype_timestamp, dtype_timestamp, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, dtype_timestamp}, // dtype_blob {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -280,7 +286,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_array {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -288,7 +294,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_int64 {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -296,7 +302,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -304,7 +310,7 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_boolean {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -312,7 +318,23 @@ const BYTE DSC_add_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT} + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, + + // dtype_dec64 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, + dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, + + // dtype_dec128 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, dtype_sql_date, dtype_sql_time, + dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128} }; @@ -325,12 +347,12 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = { /* - dtype_unknown dtype_text dtype_cstring dtype_varying - 4 (unused) 5 (unused) dtype_packed dtype_byte - dtype_short dtype_long dtype_quad dtype_real + dtype_unknown dtype_text dtype_cstring dtype_varying + 4 (unused) 5 (unused) dtype_packed dtype_byte + dtype_short dtype_long dtype_quad dtype_real dtype_double dtype_d_float dtype_sql_date dtype_sql_time - dtype_timestamp dtype_blob dtype_array dtype_int64 - dtype_dbkey, dtype_boolean + dtype_timestamp dtype_blob dtype_array dtype_int64 + dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 */ // dtype_unknown @@ -339,7 +361,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_unknown, dtype_unknown, DTYPE_CANNOT, dtype_unknown, dtype_unknown, dtype_unknown, dtype_unknown, dtype_unknown, dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, dtype_unknown}, // dtype_text {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -347,7 +369,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -355,7 +377,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -363,7 +385,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -371,7 +393,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // 5 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -379,7 +401,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_packed {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -387,7 +409,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_byte {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -395,7 +417,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_short {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -403,7 +425,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -411,7 +433,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -419,7 +441,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_real {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -427,7 +449,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_double {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -435,7 +457,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_d_float -- VMS deprecated {dtype_unknown, dtype_d_float, dtype_d_float, dtype_d_float, @@ -443,7 +465,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_d_float, dtype_d_float, DTYPE_CANNOT, dtype_d_float, dtype_d_float, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, dtype_d_float}, // dtype_sql_date {dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -451,7 +473,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_sql_date, dtype_sql_date, DTYPE_CANNOT, dtype_sql_date, dtype_sql_date, dtype_sql_date, dtype_long, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_date, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_date, dtype_sql_date}, // dtype_sql_time {dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -459,7 +481,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_sql_time, dtype_sql_time, DTYPE_CANNOT, dtype_sql_time, dtype_sql_time, dtype_sql_time, DTYPE_CANNOT, dtype_long, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_time, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_sql_time, dtype_sql_time}, // dtype_timestamp {dtype_unknown, dtype_timestamp, dtype_timestamp, dtype_timestamp, @@ -467,7 +489,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_timestamp, dtype_timestamp, DTYPE_CANNOT, dtype_timestamp, dtype_timestamp, dtype_timestamp, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_timestamp, dtype_timestamp}, // dtype_blob {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -475,7 +497,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_array {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -483,7 +505,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_int64 {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -491,7 +513,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -499,7 +521,7 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_boolean {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -507,7 +529,24 @@ const BYTE DSC_sub_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT} + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, + + // dtype_dec64 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, + + // dtype_dec128 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128} + }; @@ -520,12 +559,12 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = { /* - dtype_unknown dtype_text dtype_cstring dtype_varying - 4 (unused) 5 (unused) dtype_packed dtype_byte - dtype_short dtype_long dtype_quad dtype_real + dtype_unknown dtype_text dtype_cstring dtype_varying + 4 (unused) 5 (unused) dtype_packed dtype_byte + dtype_short dtype_long dtype_quad dtype_real dtype_double dtype_d_float dtype_sql_date dtype_sql_time - dtype_timestamp dtype_blob dtype_array dtype_int64 - dtype_dbkey, dtype_boolean + dtype_timestamp dtype_blob dtype_array dtype_int64 + dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 */ // dtype_unknown @@ -534,7 +573,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_unknown, dtype_unknown, DTYPE_CANNOT, dtype_unknown, dtype_unknown, dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, dtype_unknown}, // dtype_text {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -542,7 +581,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -550,7 +589,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -558,7 +597,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -566,7 +605,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // 5 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -574,7 +613,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_packed {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -582,7 +621,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_byte {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -590,7 +629,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_short {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -598,7 +637,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -606,7 +645,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -614,7 +653,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_real {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -622,7 +661,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_double {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -630,7 +669,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_d_float -- VMS deprecated {dtype_unknown, dtype_d_float, dtype_d_float, dtype_d_float, @@ -638,7 +677,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_d_float, dtype_d_float, DTYPE_CANNOT, dtype_d_float, dtype_d_float, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, dtype_d_float}, // dtype_sql_date {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -646,7 +685,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_sql_time {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -654,7 +693,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_timestamp {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -662,7 +701,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_blob {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -670,7 +709,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_array {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -678,7 +717,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_int64 {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -686,7 +725,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_int64, dtype_int64, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_int64, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -694,7 +733,7 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_boolean {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -702,7 +741,23 @@ const BYTE DSC_multiply_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT} + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, + + // dtype_dec64 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, + + // dtype_dec128 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128} }; @@ -715,12 +770,12 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = { /* - dtype_unknown dtype_text dtype_cstring dtype_varying - 4 (unused) 5 (unused) dtype_packed dtype_byte - dtype_short dtype_long dtype_quad dtype_real + dtype_unknown dtype_text dtype_cstring dtype_varying + 4 (unused) 5 (unused) dtype_packed dtype_byte + dtype_short dtype_long dtype_quad dtype_real dtype_double dtype_d_float dtype_sql_date dtype_sql_time - dtype_timestamp dtype_blob dtype_array dtype_int64 - dtype_dbkey, dtype_boolean + dtype_timestamp dtype_blob dtype_array dtype_int64 + dtype_dbkey dtype_boolean dtype_dec64 dtype_dec128 */ // dtype_unknown @@ -729,7 +784,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_unknown, dtype_unknown, DTYPE_CANNOT, dtype_unknown, dtype_unknown, dtype_unknown, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_unknown, dtype_unknown}, // dtype_text {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -737,7 +792,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_cstring {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -745,7 +800,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_varying {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -753,7 +808,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // 4 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -761,7 +816,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // 5 (unused) {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -769,7 +824,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_packed {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -777,7 +832,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_byte {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -785,7 +840,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_short {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -793,7 +848,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_long, dtype_long, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_long {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -801,7 +856,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_long, dtype_long, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_quad {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -809,7 +864,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_real {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -817,7 +872,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_double {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -825,7 +880,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, dtype_double}, // dtype_d_float -- VMS deprecated {dtype_unknown, dtype_d_float, dtype_d_float, dtype_d_float, @@ -833,7 +888,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_d_float, dtype_d_float, DTYPE_CANNOT, dtype_d_float, dtype_d_float, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_d_float, dtype_d_float}, // dtype_sql_date {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -841,7 +896,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_sql_time {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -849,7 +904,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_timestamp {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -857,7 +912,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_blob {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -865,7 +920,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_array {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -873,7 +928,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_int64 {dtype_unknown, dtype_double, dtype_double, dtype_double, @@ -881,7 +936,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = dtype_double, dtype_double, DTYPE_CANNOT, dtype_double, dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_double, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, // dtype_dbkey {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -889,7 +944,7 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT}, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, // dtype_boolean {DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, @@ -897,7 +952,23 @@ const BYTE DSC_multiply_blr4_result[DTYPE_TYPE_MAX][DTYPE_TYPE_MAX] = DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, - DTYPE_CANNOT, DTYPE_CANNOT} + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT}, + + // dtype_dec64 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128}, + + // dtype_dec128 + {dtype_unknown, dtype_dec128, dtype_dec128, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, + dtype_dec128, dtype_dec128, DTYPE_CANNOT, dtype_double, + dtype_double, dtype_d_float, DTYPE_CANNOT, DTYPE_CANNOT, + DTYPE_CANNOT, DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, + DTYPE_CANNOT, DTYPE_CANNOT, dtype_dec128, dtype_dec128} }; @@ -1003,6 +1074,16 @@ bool DSC_make_descriptor(DSC* desc, desc->dsc_dtype = dtype_double; break; + case blr_dec64: + desc->dsc_length = sizeof(Decimal64); + desc->dsc_dtype = dtype_dec64; + break; + + case blr_dec128: + desc->dsc_length = sizeof(Decimal128); + desc->dsc_dtype = dtype_dec128; + break; + case blr_timestamp: desc->dsc_length = 2 * sizeof(SLONG); desc->dsc_dtype = dtype_timestamp; @@ -1164,6 +1245,10 @@ const char* dsc::typeToText() const return "dbkey"; case dtype_boolean: return "boolean"; + case dtype_dec64: + return "decfloat(16)"; + case dtype_dec128: + return "decfloat(34)"; default: return "out of range"; } @@ -1252,6 +1337,16 @@ void dsc::getSqlInfo(SLONG* sqlLength, SLONG* sqlSubType, SLONG* sqlScale, SLONG *sqlType = SQL_BOOLEAN; break; + case dtype_dec64: + *sqlType = SQL_DEC16; + *sqlScale = 0; + break; + + case dtype_dec128: + *sqlType = SQL_DEC34; + *sqlScale = 0; + break; + default: status_exception::raise( Arg::Gds(isc_sqlerr) << Arg::Num(-804) << diff --git a/src/common/dsc.h b/src/common/dsc.h index d2eb5a196e..29e77d16c4 100644 --- a/src/common/dsc.h +++ b/src/common/dsc.h @@ -31,6 +31,7 @@ #include "consts_pub.h" #include "../jrd/ods.h" #include "../intl/charsets.h" +#include "../common/DecFloat.h" // Data type information @@ -67,9 +68,14 @@ inline bool DTYPE_IS_APPROX(UCHAR d) return d == dtype_double || d == dtype_real; } +inline bool DTYPE_IS_DECFLOAT(UCHAR d) +{ + return d == dtype_dec128 || d == dtype_dec64; +} + inline bool DTYPE_IS_NUMERIC(UCHAR d) { - return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64; + return (d >= dtype_byte && d <= dtype_d_float) || d == dtype_int64 || DTYPE_IS_DECFLOAT(d); } // Descriptor format @@ -155,6 +161,21 @@ typedef struct dsc return dsc_dtype >= dtype_sql_date && dsc_dtype <= dtype_timestamp; } + bool isDecFloat() const + { + return DTYPE_IS_DECFLOAT(dsc_dtype); + } + + bool isDecOrInt() const + { + return isDecFloat() || isExact(); + } + + bool isApprox() const + { + return DTYPE_IS_APPROX(dsc_dtype); + } + bool isUnknown() const { return dsc_dtype == dtype_unknown; @@ -259,6 +280,22 @@ typedef struct dsc dsc_address = (UCHAR*) address; } + void makeDecimal64(Firebird::Decimal64* address = NULL) + { + clear(); + dsc_dtype = dtype_dec64; + dsc_length = sizeof(Firebird::Decimal64); + dsc_address = (UCHAR*) address; + } + + void makeDecimal128(Firebird::Decimal128* address = NULL) + { + clear(); + dsc_dtype = dtype_dec128; + dsc_length = sizeof(Firebird::Decimal128); + dsc_address = (UCHAR*) address; + } + void makeInt64(SCHAR scale, SINT64* address = NULL) { clear(); diff --git a/src/common/dsc_pub.h b/src/common/dsc_pub.h index 6589b4d09b..57bf375a8a 100644 --- a/src/common/dsc_pub.h +++ b/src/common/dsc_pub.h @@ -61,7 +61,9 @@ #define dtype_int64 19 #define dtype_dbkey 20 #define dtype_boolean 21 -#define DTYPE_TYPE_MAX 22 +#define dtype_dec64 22 +#define dtype_dec128 23 +#define DTYPE_TYPE_MAX 24 #define ISC_TIME_SECONDS_PRECISION 10000 #define ISC_TIME_SECONDS_PRECISION_SCALE (-4) diff --git a/src/common/sdl.cpp b/src/common/sdl.cpp index e935a7583a..e813bc0896 100644 --- a/src/common/sdl.cpp +++ b/src/common/sdl.cpp @@ -845,6 +845,16 @@ static const UCHAR* sdl_desc(const UCHAR* ptr, DSC* desc) desc->dsc_length = sizeof(double); break; + case blr_dec64: + desc->dsc_dtype = dtype_dec64; + desc->dsc_length = sizeof(Decimal64); + break; + + case blr_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + break; + case blr_timestamp: desc->dsc_dtype = dtype_timestamp; desc->dsc_length = sizeof(ISC_QUAD); diff --git a/src/common/utils.cpp b/src/common/utils.cpp index 0e06968a7b..76c8cd1758 100644 --- a/src/common/utils.cpp +++ b/src/common/utils.cpp @@ -1480,89 +1480,66 @@ void logAndDie(const char* text) #endif } +UCHAR sqlTypeToDscType(SSHORT sqlType) +{ + switch (sqlType) + { + case SQL_VARYING: + return dtype_varying; + case SQL_TEXT: + return dtype_text; + case SQL_NULL: + return dtype_text; + case SQL_DOUBLE: + return dtype_double; + case SQL_FLOAT: + return dtype_real; + case SQL_D_FLOAT: + return dtype_d_float; + case SQL_TYPE_DATE: + return dtype_sql_date; + case SQL_TYPE_TIME: + return dtype_sql_time; + case SQL_TIMESTAMP: + return dtype_timestamp; + case SQL_BLOB: + return dtype_blob; + case SQL_ARRAY: + return dtype_array; + case SQL_LONG: + return dtype_long; + case SQL_SHORT: + return dtype_short; + case SQL_INT64: + return dtype_int64; + case SQL_QUAD: + return dtype_quad; + case SQL_BOOLEAN: + return dtype_boolean; + case SQL_DEC16: + return dtype_dec64; + case SQL_DEC34: + return dtype_dec128; + default: + return dtype_unknown; + } +} + unsigned sqlTypeToDsc(unsigned runOffset, unsigned sqlType, unsigned sqlLength, unsigned* dtype, unsigned* len, unsigned* offset, unsigned* nullOffset) { sqlType &= ~1; - unsigned dscType; + unsigned dscType = sqlTypeToDscType(sqlType); - switch (sqlType) + if (dscType == dtype_unknown) { - case SQL_VARYING: - dscType = dtype_varying; - break; - - case SQL_TEXT: - dscType = dtype_text; - break; - - case SQL_DOUBLE: - dscType = dtype_double; - break; - - case SQL_FLOAT: - dscType = dtype_real; - break; - - case SQL_D_FLOAT: - dscType = dtype_d_float; - break; - - case SQL_TYPE_DATE: - dscType = dtype_sql_date; - break; - - case SQL_TYPE_TIME: - dscType = dtype_sql_time; - break; - - case SQL_TIMESTAMP: - dscType = dtype_timestamp; - break; - - case SQL_BLOB: - dscType = dtype_blob; - break; - - case SQL_ARRAY: - dscType = dtype_array; - break; - - case SQL_LONG: - dscType = dtype_long; - break; - - case SQL_SHORT: - dscType = dtype_short; - break; - - case SQL_INT64: - dscType = dtype_int64; - break; - - case SQL_QUAD: - dscType = dtype_quad; - break; - - case SQL_BOOLEAN: - dscType = dtype_boolean; - break; - - case SQL_NULL: - dscType = dtype_text; - break; - - default: fb_assert(false); // keep old yvalve logic dscType = sqlType; - break; } if (dtype) - { *dtype = dscType; - } if (sqlType == SQL_VARYING) sqlLength += sizeof(USHORT); diff --git a/src/common/utils_proto.h b/src/common/utils_proto.h index 6acfa9e63d..e92efb5abe 100644 --- a/src/common/utils_proto.h +++ b/src/common/utils_proto.h @@ -184,6 +184,9 @@ namespace fb_utils void logAndDie(const char* text); + // On incorrect sqlType returns dsc_unknown + UCHAR sqlTypeToDscType(SSHORT sqlType); + // Returns next offset value unsigned sqlTypeToDsc(unsigned prevOffset, unsigned sqlType, unsigned sqlLength, unsigned* dtype, unsigned* len, unsigned* offset, unsigned* nullOffset); diff --git a/src/common/xdr.cpp b/src/common/xdr.cpp index 1dfff76e99..48cf627d7e 100644 --- a/src/common/xdr.cpp +++ b/src/common/xdr.cpp @@ -30,6 +30,7 @@ #include "../common/xdr_proto.h" #include "../yvalve/gds_proto.h" #include "../common/gdsassert.h" +#include "../common/DecFloat.h" inline UCHAR* XDR_ALLOC(ULONG size) { @@ -104,7 +105,7 @@ bool_t xdr_hyper( XDR* xdrs, void* pi64) { /************************************** * - * x d r _ h y p e r ( n o n - S O L A R I S ) + * x d r _ h y p e r * ************************************** * @@ -112,13 +113,6 @@ bool_t xdr_hyper( XDR* xdrs, void* pi64) * Map a 64-bit Integer from external to internal representation * (or vice versa). * - * Enable this for all platforms except Solaris (since it is - * available in the XDR library on Solaris). This function (normally) - * would have been implemented in REMOTE/xdr.c. Since some system - * XDR libraries (HP-UX) do not implement this function, we have it - * in this module. At a later date, when the function is available - * on all platforms, we can start using the system-provided version. - * * Handles "swapping" of the 2 long's to be "Endian" sensitive. * **************************************/ @@ -259,6 +253,18 @@ bool_t xdr_datum( XDR* xdrs, const dsc* desc, UCHAR* buffer) return FALSE; break; + case dtype_dec64: + fb_assert(desc->dsc_length >= sizeof(Firebird::Decimal64)); + if (!xdr_dec64(xdrs, reinterpret_cast(p))) + return FALSE; + break; + + case dtype_dec128: + fb_assert(desc->dsc_length >= sizeof(Firebird::Decimal128)); + if (!xdr_dec128(xdrs, reinterpret_cast(p))) + return FALSE; + break; + case dtype_timestamp: fb_assert(desc->dsc_length >= 2 * sizeof(SLONG)); if (!xdr_long(xdrs, &((SLONG*) p)[0])) @@ -337,6 +343,25 @@ bool_t xdr_double(XDR* xdrs, double* ip) } +bool_t xdr_dec64(XDR* xdrs, Firebird::Decimal64* ip) +{ + return xdr_hyper(xdrs, ip->getBytes()); +} + + +bool_t xdr_dec128(XDR* xdrs, Firebird::Decimal128* ip) +{ + UCHAR* bytes = ip->getBytes(); + +#ifndef WORDS_BIGENDIAN + return xdr_hyper(xdrs, &bytes[8]) && xdr_hyper(xdrs, &bytes[0]); +#else + fb_assert(false); // Dec64/128 XDR not tested on bigendians! + return xdr_hyper(xdrs, &bytes[0]) && xdr_hyper(xdrs, &bytes[8]); +#endif +} + + bool_t xdr_enum(XDR* xdrs, xdr_op* ip) { /************************************** diff --git a/src/common/xdr_proto.h b/src/common/xdr_proto.h index 0e063b2395..b9387e5363 100644 --- a/src/common/xdr_proto.h +++ b/src/common/xdr_proto.h @@ -27,12 +27,10 @@ #include "../common/dsc.h" #include "../common/xdr.h" -// 15 Jan 2003. Nickolay Samofatov -// Functions below need to have C++ linkage to avoid name clash with -// standard XDR. Firebird XDR is NOT compatible with Sun XDR at interface level - bool_t xdr_datum(XDR*, const dsc*, UCHAR*); bool_t xdr_double(XDR*, double*); +bool_t xdr_dec64(XDR*, Firebird::Decimal64*); +bool_t xdr_dec128(XDR*, Firebird::Decimal128*); bool_t xdr_enum(XDR*, xdr_op*); bool_t xdr_float(XDR*, float*); bool_t xdr_int(XDR*, int*); diff --git a/src/dsql/AggNodes.cpp b/src/dsql/AggNodes.cpp index f290728268..c095a0bc2b 100644 --- a/src/dsql/AggNodes.cpp +++ b/src/dsql/AggNodes.cpp @@ -281,7 +281,7 @@ bool AggNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const AggNode* o = other->as(); + const AggNode* o = nodeAs(other); fb_assert(o); // ASF: We compare name address. That should be ok, as we have only one AggInfo instance @@ -385,7 +385,7 @@ bool AggNode::aggPass(thread_db* tdbb, jrd_req* request) const to.dsc_sub_type = 0; to.dsc_scale = 0; to.dsc_ttype() = ttype_sort_key; - to.dsc_length = asb->keyItems[0].skd_length; + to.dsc_length = asb->keyItems[0].getSkdLength(); to.dsc_address = data; INTL_string_to_key(tdbb, INTL_TEXT_TO_INDEX(desc->getTextType()), desc, &to, INTL_KEY_UNIQUE); @@ -393,7 +393,7 @@ bool AggNode::aggPass(thread_db* tdbb, jrd_req* request) const dsc toDesc = asb->desc; toDesc.dsc_address = data + - (asb->intl ? asb->keyItems[1].skd_offset : 0); + (asb->intl ? asb->keyItems[1].getSkdOffset() : 0); MOV_move(tdbb, desc, &toDesc); // dimitr: Here we add a monotonically increasing value to the sort record. @@ -455,7 +455,7 @@ dsc* AggNode::execute(thread_db* tdbb, jrd_req* request) const break; } - desc.dsc_address = data + (asb->intl ? asb->keyItems[1].skd_offset : 0); + desc.dsc_address = data + (asb->intl ? asb->keyItems[1].getSkdOffset() : 0); aggPass(tdbb, request, &desc); } @@ -494,7 +494,12 @@ void AvgAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) if (desc->isNull()) return; - if (dialect1) + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + } + else if (dialect1) { if (!DTYPE_IS_NUMERIC(desc->dsc_dtype) && !DTYPE_IS_TEXT(desc->dsc_dtype)) { @@ -531,6 +536,17 @@ void AvgAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { arg->getDesc(tdbb, csb, desc); + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + nodFlags |= FLAG_DECFLOAT; + return; + } + if (dialect1) { if (!(DTYPE_IS_NUMERIC(desc->dsc_dtype) || DTYPE_IS_TEXT(desc->dsc_dtype))) @@ -601,7 +617,7 @@ AggNode* AvgAggNode::pass2(thread_db* tdbb, CompilerScratch* csb) { AggNode::pass2(tdbb, csb); - if (dialect1) + if (dialect1 && !(nodFlags & FLAG_DECFLOAT)) nodFlags |= FLAG_DOUBLE; // We need a second descriptor in the impure area for AVG. @@ -659,15 +675,23 @@ dsc* AvgAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const dsc temp; SINT64 i; double d; + Decimal128 dec; if (!dialect1 && impure->vlu_desc.dsc_dtype == dtype_int64) { i = *((SINT64*) impure->vlu_desc.dsc_address) / impure->vlux_count; temp.makeInt64(impure->vlu_desc.dsc_scale, &i); } + else if (DTYPE_IS_DECFLOAT(impure->vlu_desc.dsc_dtype)) + { + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + dec.set(impure->vlux_count, decSt, 0); + dec = MOV_get_dec128(tdbb, &impure->vlu_desc).div(decSt, dec); + temp.makeDecimal128(&dec); + } else { - d = MOV_get_double(&impure->vlu_desc) / impure->vlux_count; + d = MOV_get_double(tdbb, &impure->vlu_desc) / impure->vlux_count; temp.makeDouble(&d); } @@ -961,7 +985,12 @@ void SumAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) if (desc->isNull()) return; - if (dialect1) + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + } + else if (dialect1) { if (!DTYPE_IS_NUMERIC(desc->dsc_dtype) && !DTYPE_IS_TEXT(desc->dsc_dtype)) { @@ -1008,6 +1037,16 @@ void SumAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { arg->getDesc(tdbb, csb, desc); + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + nodFlags |= FLAG_DECFLOAT; + return; + } + if (dialect1) { switch (desc->dsc_dtype) @@ -1237,7 +1276,7 @@ void MaxMinAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const return; } - const int result = MOV_compare(desc, &impure->vlu_desc); + const int result = MOV_compare(tdbb, desc, &impure->vlu_desc); if ((type == TYPE_MAX && result > 0) || (type == TYPE_MIN && result < 0)) EVL_make_value(tdbb, desc, impure); @@ -1290,13 +1329,32 @@ void StdDevAggNode::parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned /* void StdDevAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { - desc->makeDouble(); + MAKE_desc(dsqlScratch, desc, arg); desc->setNullable(true); + + if (desc->isNull()) + return; + + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + desc->makeDecimal128(); + else + desc->makeDouble(); } void StdDevAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { - desc->makeDouble(); + arg->getDesc(tdbb, csb, desc); + + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->makeDecimal128(); + nodFlags |= FLAG_DECFLOAT; + } + else + { + desc->makeDouble(); + nodFlags |= FLAG_DOUBLE; + } } ValueExprNode* StdDevAggNode::copy(thread_db* tdbb, NodeCopier& copier) const @@ -1331,10 +1389,18 @@ void StdDevAggNode::aggInit(thread_db* tdbb, jrd_req* request) const AggNode::aggInit(tdbb, request); impure_value_ex* impure = request->getImpure(impureOffset); - impure->make_double(0); - StdDevImpure* impure2 = request->getImpure(impure2Offset); - impure2->x = impure2->x2 = 0.0; + + if (nodFlags & FLAG_DECFLOAT) + { + impure->make_decimal128(CDecimal128(0)); + impure2->dec.x = impure2->dec.x2 = CDecimal128(0); + } + else + { + impure->make_double(0); + impure2->dbl.x = impure2->dbl.x2 = 0.0; + } } void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const @@ -1342,11 +1408,22 @@ void StdDevAggNode::aggPass(thread_db* tdbb, jrd_req* request, dsc* desc) const impure_value_ex* impure = request->getImpure(impureOffset); ++impure->vlux_count; - const double d = MOV_get_double(desc); - StdDevImpure* impure2 = request->getImpure(impure2Offset); - impure2->x += d; - impure2->x2 += d * d; + if (nodFlags & FLAG_DECFLOAT) + { + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + const Decimal128 d = MOV_get_dec128(tdbb, desc); + + impure2->dec.x = impure2->dec.x.add(decSt, d); + impure2->dec.x2 = impure2->dec.x2.fma(decSt, d, d); + } + else + { + const double d = MOV_get_double(tdbb, desc); + + impure2->dbl.x += d; + impure2->dbl.x2 += d * d; + } } dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const @@ -1354,6 +1431,12 @@ dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const impure_value_ex* impure = request->getImpure(impureOffset); StdDevImpure* impure2 = request->getImpure(impure2Offset); double d; + Decimal128 dec; + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + Decimal128 cnt; + if (nodFlags & FLAG_DECFLOAT) + cnt.set(impure->vlux_count, decSt, 0); switch (type) { @@ -1362,11 +1445,25 @@ dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const if (impure->vlux_count < 2) return NULL; - d = (impure2->x2 - impure2->x * impure2->x / impure->vlux_count) / - (impure->vlux_count - 1); + if (nodFlags & FLAG_DECFLOAT) + { + Decimal128 cntMinus1; + cntMinus1.set(impure->vlux_count - 1, decSt, 0); + dec = impure2->dec.x.mul(decSt, impure2->dec.x).div(decSt, cnt); + dec = impure2->dec.x2.sub(decSt, dec); + dec = dec.div(decSt, cntMinus1); - if (type == TYPE_STDDEV_SAMP) - d = sqrt(d); + if (type == TYPE_STDDEV_SAMP) + dec = dec.sqrt(decSt); + } + else + { + d = (impure2->dbl.x2 - impure2->dbl.x * impure2->dbl.x / impure->vlux_count) / + (impure->vlux_count - 1); + + if (type == TYPE_STDDEV_SAMP) + d = sqrt(d); + } break; case TYPE_STDDEV_POP: @@ -1374,18 +1471,33 @@ dsc* StdDevAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const if (impure->vlux_count == 0) return NULL; - d = (impure2->x2 - impure2->x * impure2->x / impure->vlux_count) / - impure->vlux_count; + if (nodFlags & FLAG_DECFLOAT) + { + dec = impure2->dec.x.mul(decSt, impure2->dec.x).div(decSt, cnt); + dec = impure2->dec.x2.sub(decSt, dec); + dec = dec.div(decSt, cnt); - if (type == TYPE_STDDEV_POP) - d = sqrt(d); + if (type == TYPE_STDDEV_SAMP) + dec = dec.sqrt(decSt); + } + else + { + d = (impure2->dbl.x2 - impure2->dbl.x * impure2->dbl.x / impure->vlux_count) / + impure->vlux_count; + + if (type == TYPE_STDDEV_POP) + d = sqrt(d); + } break; } dsc temp; - temp.makeDouble(&d); - EVL_make_value(tdbb, &temp, impure); + if (nodFlags & FLAG_DECFLOAT) + temp.makeDecimal128(&dec); + else + temp.makeDouble(&d); + EVL_make_value(tdbb, &temp, impure); return &impure->vlu_desc; } @@ -1426,13 +1538,32 @@ void CorrAggNode::parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned /*co void CorrAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { - desc->makeDouble(); + MAKE_desc(dsqlScratch, desc, arg); desc->setNullable(true); + + if (desc->isNull()) + return; + + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + desc->makeDecimal128(); + else + desc->makeDouble(); } void CorrAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { - desc->makeDouble(); + arg->getDesc(tdbb, csb, desc); + + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->makeDecimal128(); + nodFlags |= FLAG_DECFLOAT; + } + else + { + desc->makeDouble(); + nodFlags |= FLAG_DOUBLE; + } } ValueExprNode* CorrAggNode::copy(thread_db* tdbb, NodeCopier& copier) const @@ -1468,10 +1599,18 @@ void CorrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const AggNode::aggInit(tdbb, request); impure_value_ex* impure = request->getImpure(impureOffset); - impure->make_double(0); - CorrImpure* impure2 = request->getImpure(impure2Offset); - impure2->x = impure2->x2 = impure2->y = impure2->y2 = impure2->xy = 0.0; + + if (nodFlags & FLAG_DECFLOAT) + { + impure->make_decimal128(CDecimal128(0)); + impure2->dec.x = impure2->dec.x2 = impure2->dec.y = impure2->dec.y2 = impure2->dec.xy = CDecimal128(0); + } + else + { + impure->make_double(0); + impure2->dbl.x = impure2->dbl.x2 = impure2->dbl.y = impure2->dbl.y2 = impure2->dbl.xy = 0.0; + } } bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const @@ -1490,16 +1629,30 @@ bool CorrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return false; ++impure->vlux_count; - - const double y = MOV_get_double(desc); - const double x = MOV_get_double(desc2); - CorrImpure* impure2 = request->getImpure(impure2Offset); - impure2->x += x; - impure2->x2 += x * x; - impure2->y += y; - impure2->y2 += y * y; - impure2->xy += x * y; + + if (nodFlags & FLAG_DECFLOAT) + { + const Decimal128 y = MOV_get_dec128(tdbb, desc); + const Decimal128 x = MOV_get_dec128(tdbb, desc2); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + impure2->dec.x = impure2->dec.x.add(decSt, x); + impure2->dec.x2 = impure2->dec.x2.fma(decSt, x, x); + impure2->dec.y = impure2->dec.y.add(decSt, y); + impure2->dec.y2 = impure2->dec.y2.fma(decSt, y, y); + impure2->dec.xy = impure2->dec.xy.fma(decSt, x, y); + } + else + { + const double y = MOV_get_double(tdbb, desc); + const double x = MOV_get_double(tdbb, desc2); + impure2->dbl.x += x; + impure2->dbl.x2 += x * x; + impure2->dbl.y += y; + impure2->dbl.y2 += y * y; + impure2->dbl.xy += x * y; + } return true; } @@ -1514,19 +1667,43 @@ dsc* CorrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const impure_value_ex* impure = request->getImpure(impureOffset); CorrImpure* impure2 = request->getImpure(impure2Offset); double d; + Decimal128 dec; + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + Decimal128 cnt; + if (nodFlags & FLAG_DECFLOAT) + cnt.set(impure->vlux_count, decSt, 0); switch (type) { case TYPE_COVAR_SAMP: if (impure->vlux_count < 2) return NULL; - d = (impure2->xy - impure2->y * impure2->x / impure->vlux_count) / (impure->vlux_count - 1); + + if (nodFlags & FLAG_DECFLOAT) + { + Decimal128 cntMinus1; + cntMinus1.set(impure->vlux_count - 1, decSt, 0); + dec = impure2->dec.x.mul(decSt, impure2->dec.y).div(decSt, cnt); + dec = impure2->dec.xy.sub(decSt, dec); + dec = dec.div(decSt, cntMinus1); + } + else + d = (impure2->dbl.xy - impure2->dbl.y * impure2->dbl.x / impure->vlux_count) / (impure->vlux_count - 1); break; case TYPE_COVAR_POP: if (impure->vlux_count == 0) return NULL; - d = (impure2->xy - impure2->y * impure2->x / impure->vlux_count) / impure->vlux_count; + + if (nodFlags & FLAG_DECFLOAT) + { + dec = impure2->dec.x.mul(decSt, impure2->dec.y).div(decSt, cnt); + dec = impure2->dec.xy.sub(decSt, dec); + dec = dec.div(decSt, cnt); + } + else + d = (impure2->dbl.xy - impure2->dbl.y * impure2->dbl.x / impure->vlux_count) / impure->vlux_count; break; case TYPE_CORR: @@ -1535,24 +1712,51 @@ dsc* CorrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const if (impure->vlux_count == 0) return NULL; - const double covarPop = (impure2->xy - impure2->y * impure2->x / impure->vlux_count) / - impure->vlux_count; - const double varPopX = (impure2->x2 - impure2->x * impure2->x / impure->vlux_count) / - impure->vlux_count; - const double varPopY = (impure2->y2 - impure2->y * impure2->y / impure->vlux_count) / - impure->vlux_count; - const double divisor = sqrt(varPopX) * sqrt(varPopY); + if (nodFlags & FLAG_DECFLOAT) + { + dec = impure2->dec.x.mul(decSt, impure2->dec.y).div(decSt, cnt); + dec = impure2->dec.xy.sub(decSt, dec); + const Decimal128 covarPop = dec.div(decSt, cnt); - if (divisor == 0.0) - return NULL; + dec = impure2->dec.x.mul(decSt, impure2->dec.x).div(decSt, cnt); + dec = impure2->dec.x2.sub(decSt, dec); + const Decimal128 varPopX = dec.div(decSt, cnt); - d = covarPop / divisor; + dec = impure2->dec.y.mul(decSt, impure2->dec.y).div(decSt, cnt); + dec = impure2->dec.y2.sub(decSt, dec); + const Decimal128 varPopY = dec.div(decSt, cnt); + + const Decimal128 divisor = varPopX.sqrt(decSt).mul(decSt, varPopY.sqrt(decSt)); + + if (divisor.compare(decSt, CDecimal128(0)) == 0) + return NULL; + + dec = covarPop.div(decSt, divisor); + } + else + { + const double covarPop = (impure2->dbl.xy - impure2->dbl.y * impure2->dbl.x / impure->vlux_count) / + impure->vlux_count; + const double varPopX = (impure2->dbl.x2 - impure2->dbl.x * impure2->dbl.x / impure->vlux_count) / + impure->vlux_count; + const double varPopY = (impure2->dbl.y2 - impure2->dbl.y * impure2->dbl.y / impure->vlux_count) / + impure->vlux_count; + const double divisor = sqrt(varPopX) * sqrt(varPopY); + + if (divisor == 0.0) + return NULL; + + d = covarPop / divisor; + } break; } } dsc temp; - temp.makeDouble(&d); + if (nodFlags & FLAG_DECFLOAT) + temp.makeDecimal128(&dec); + else + temp.makeDouble(&d); EVL_make_value(tdbb, &temp, impure); @@ -1612,13 +1816,32 @@ void RegrAggNode::parseArgs(thread_db* tdbb, CompilerScratch* csb, unsigned /*co void RegrAggNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { - desc->makeDouble(); + MAKE_desc(dsqlScratch, desc, arg); desc->setNullable(true); + + if (desc->isNull()) + return; + + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + desc->makeDecimal128(); + else + desc->makeDouble(); } void RegrAggNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { - desc->makeDouble(); + arg->getDesc(tdbb, csb, desc); + + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->makeDecimal128(); + nodFlags |= FLAG_DECFLOAT; + } + else + { + desc->makeDouble(); + nodFlags |= FLAG_DOUBLE; + } } ValueExprNode* RegrAggNode::copy(thread_db* tdbb, NodeCopier& copier) const @@ -1654,10 +1877,18 @@ void RegrAggNode::aggInit(thread_db* tdbb, jrd_req* request) const AggNode::aggInit(tdbb, request); impure_value_ex* impure = request->getImpure(impureOffset); - impure->make_double(0); - RegrImpure* impure2 = request->getImpure(impure2Offset); - impure2->x = impure2->x2 = impure2->y = impure2->y2 = impure2->xy = 0.0; + + if (nodFlags & FLAG_DECFLOAT) + { + impure->make_decimal128(CDecimal128(0)); + impure2->dec.x = impure2->dec.x2 = impure2->dec.y = impure2->dec.y2 = impure2->dec.xy = CDecimal128(0); + } + else + { + impure->make_double(0); + impure2->dbl.x = impure2->dbl.x2 = impure2->dbl.y = impure2->dbl.y2 = impure2->dbl.xy = 0.0; + } } bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const @@ -1676,16 +1907,31 @@ bool RegrAggNode::aggPass(thread_db* tdbb, jrd_req* request) const return false; ++impure->vlux_count; - - const double y = MOV_get_double(desc); - const double x = MOV_get_double(desc2); - RegrImpure* impure2 = request->getImpure(impure2Offset); - impure2->x += x; - impure2->x2 += x * x; - impure2->y += y; - impure2->y2 += y * y; - impure2->xy += x * y; + + if (nodFlags & FLAG_DECFLOAT) + { + const Decimal128 y = MOV_get_dec128(tdbb, desc); + const Decimal128 x = MOV_get_dec128(tdbb, desc2); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + impure2->dec.x = impure2->dec.x.add(decSt, x); + impure2->dec.x2 = impure2->dec.x2.fma(decSt, x, x); + impure2->dec.y = impure2->dec.y.add(decSt, y); + impure2->dec.y2 = impure2->dec.y2.fma(decSt, y, y); + impure2->dec.xy = impure2->dec.xy.fma(decSt, x, y); + } + else + { + const double y = MOV_get_double(tdbb, desc); + const double x = MOV_get_double(tdbb, desc2); + + impure2->dbl.x += x; + impure2->dbl.x2 += x * x; + impure2->dbl.y += y; + impure2->dbl.y2 += y * y; + impure2->dbl.xy += x * y; + } return true; } @@ -1699,74 +1945,148 @@ dsc* RegrAggNode::aggExecute(thread_db* tdbb, jrd_req* request) const { impure_value_ex* impure = request->getImpure(impureOffset); RegrImpure* impure2 = request->getImpure(impure2Offset); + dsc temp; if (impure->vlux_count == 0) return NULL; - const double varPopX = (impure2->x2 - impure2->x * impure2->x / impure->vlux_count) / impure->vlux_count; - const double varPopY = (impure2->y2 - impure2->y * impure2->y / impure->vlux_count) / impure->vlux_count; - const double covarPop = (impure2->xy - impure2->y * impure2->x / impure->vlux_count) / impure->vlux_count; - const double avgX = impure2->x / impure->vlux_count; - const double avgY = impure2->y / impure->vlux_count; - const double slope = covarPop / varPopX; - const double sq = sqrt(varPopX) * sqrt(varPopY); - const double corr = covarPop / sq; - - double d; - - switch (type) + if (nodFlags & FLAG_DECFLOAT) { - case TYPE_REGR_AVGX: - d = avgX; - break; + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + Decimal128 cnt; + cnt.set(impure->vlux_count, decSt, 0); + DecimalStatus safeDivide = decSt; + safeDivide.decExtFlag &= ~DEC_Division_by_zero; - case TYPE_REGR_AVGY: - d = avgY; - break; + const Decimal128 sxx = impure2->dec.x2.sub(decSt, impure2->dec.x.mul(decSt, impure2->dec.x).div(decSt, cnt)); + const Decimal128 syy = impure2->dec.y2.sub(decSt, impure2->dec.y.mul(decSt, impure2->dec.y).div(decSt, cnt)); + const Decimal128 sxy = impure2->dec.xy.sub(decSt, impure2->dec.x.mul(decSt, impure2->dec.y).div(decSt, cnt)); + const Decimal128 varPopX = sxx.div(decSt, cnt); + const Decimal128 varPopY = syy.div(decSt, cnt); + const Decimal128 covarPop = sxy.div(decSt, cnt); + const Decimal128 avgX = impure2->dec.x.div(decSt, cnt); + const Decimal128 avgY = impure2->dec.y.div(decSt, cnt); + const Decimal128 slope = covarPop.div(safeDivide, varPopX); + const Decimal128 sq = varPopX.sqrt(decSt).mul(decSt, varPopY.sqrt(decSt)); + const Decimal128 corr = covarPop.div(safeDivide, sq); - case TYPE_REGR_INTERCEPT: - if (varPopX == 0.0) - return NULL; - else - d = avgY - slope * avgX; - break; + Decimal128 d; - case TYPE_REGR_R2: - if (varPopX == 0.0) - return NULL; - else if (varPopY == 0.0) - d = 1.0; - else if (sq == 0.0) - return NULL; - else - d = corr * corr; - break; + switch (type) + { + case TYPE_REGR_AVGX: + d = avgX; + break; - case TYPE_REGR_SLOPE: - if (varPopX == 0.0) - return NULL; - else - d = covarPop / varPopX; - break; + case TYPE_REGR_AVGY: + d = avgY; + break; - case TYPE_REGR_SXX: - d = impure->vlux_count * varPopX; - break; + case TYPE_REGR_INTERCEPT: + if (varPopX.compare(decSt, CDecimal128(0)) == 0) + return NULL; + else + d = avgY.sub(decSt, slope.mul(decSt, avgX)); + break; - case TYPE_REGR_SXY: - d = impure->vlux_count * covarPop; - break; + case TYPE_REGR_R2: + if (varPopX.compare(decSt, CDecimal128(0)) == 0) + return NULL; + else if (varPopY.compare(decSt, CDecimal128(0)) == 0) + d.set(1, decSt, 0); + else if (sq.compare(decSt, CDecimal128(0)) == 0) + return NULL; + else + d = corr.mul(decSt, corr); + break; - case TYPE_REGR_SYY: - d = impure->vlux_count * varPopY; - break; + case TYPE_REGR_SLOPE: + if (varPopX.compare(decSt, CDecimal128(0)) == 0) + return NULL; + else + d = slope; + break; + + case TYPE_REGR_SXX: + d = sxx; + break; + + case TYPE_REGR_SXY: + d = sxy; + break; + + case TYPE_REGR_SYY: + d = syy; + break; + } + + temp.makeDecimal128(&d); + } + else + { + const double varPopX = (impure2->dbl.x2 - impure2->dbl.x * impure2->dbl.x / impure->vlux_count) / impure->vlux_count; + const double varPopY = (impure2->dbl.y2 - impure2->dbl.y * impure2->dbl.y / impure->vlux_count) / impure->vlux_count; + const double covarPop = (impure2->dbl.xy - impure2->dbl.y * impure2->dbl.x / impure->vlux_count) / impure->vlux_count; + const double avgX = impure2->dbl.x / impure->vlux_count; + const double avgY = impure2->dbl.y / impure->vlux_count; + const double slope = covarPop / varPopX; + const double sq = sqrt(varPopX) * sqrt(varPopY); + const double corr = covarPop / sq; + + double d; + + switch (type) + { + case TYPE_REGR_AVGX: + d = avgX; + break; + + case TYPE_REGR_AVGY: + d = avgY; + break; + + case TYPE_REGR_INTERCEPT: + if (varPopX == 0.0) + return NULL; + else + d = avgY - slope * avgX; + break; + + case TYPE_REGR_R2: + if (varPopX == 0.0) + return NULL; + else if (varPopY == 0.0) + d = 1.0; + else if (sq == 0.0) + return NULL; + else + d = corr * corr; + break; + + case TYPE_REGR_SLOPE: + if (varPopX == 0.0) + return NULL; + else + d = covarPop / varPopX; + break; + + case TYPE_REGR_SXX: + d = impure->vlux_count * varPopX; + break; + + case TYPE_REGR_SXY: + d = impure->vlux_count * covarPop; + break; + + case TYPE_REGR_SYY: + d = impure->vlux_count * varPopY; + break; + } + + temp.makeDouble(&d); } - dsc temp; - temp.makeDouble(&d); - EVL_make_value(tdbb, &temp, impure); - return &impure->vlu_desc; } diff --git a/src/dsql/AggNodes.h b/src/dsql/AggNodes.h index 09ea54eb49..8dceaad48c 100644 --- a/src/dsql/AggNodes.h +++ b/src/dsql/AggNodes.h @@ -186,9 +186,16 @@ public: TYPE_VAR_POP }; - struct StdDevImpure + union StdDevImpure { - double x, x2; + struct + { + double x, x2; + } dbl; + struct + { + Firebird::Decimal128 x, x2; + } dec; }; explicit StdDevAggNode(MemoryPool& pool, StdDevType aType, ValueExprNode* aArg = NULL); @@ -230,9 +237,16 @@ public: TYPE_CORR }; - struct CorrImpure + union CorrImpure { - double x, x2, y, y2, xy; + struct + { + double x, x2, y, y2, xy; + } dbl; + struct + { + Firebird::Decimal128 x, x2, y, y2, xy; + } dec; }; explicit CorrAggNode(MemoryPool& pool, CorrType aType, @@ -282,9 +296,16 @@ public: TYPE_REGR_SYY }; - struct RegrImpure + union RegrImpure { - double x, x2, y, y2, xy; + struct + { + double x, x2, y, y2, xy; + } dbl; + struct + { + Firebird::Decimal128 x, x2, y, y2, xy; + } dec; }; explicit RegrAggNode(MemoryPool& pool, RegrType aType, diff --git a/src/dsql/BoolNodes.cpp b/src/dsql/BoolNodes.cpp index 9f6cefa3ee..0070c3067f 100644 --- a/src/dsql/BoolNodes.cpp +++ b/src/dsql/BoolNodes.cpp @@ -73,7 +73,7 @@ BoolExprNode* BoolExprNode::pass2(thread_db* tdbb, CompilerScratch* csb) if (csb->csb_current_nodes.hasData()) { - RseNode* topRseNode = csb->csb_current_nodes[0]->as(); + RseNode* topRseNode = nodeAs(csb->csb_current_nodes[0]); fb_assert(topRseNode); if (!topRseNode->rse_invariants) @@ -144,7 +144,7 @@ bool BinaryBoolNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!BoolExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const BinaryBoolNode* o = other->as(); + const BinaryBoolNode* o = nodeAs(other); fb_assert(o); return blrOp == o->blrOp; @@ -152,7 +152,7 @@ bool BinaryBoolNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const bool BinaryBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const { - const BinaryBoolNode* const otherNode = other->as(); + const BinaryBoolNode* const otherNode = nodeAs(other); if (!otherNode || blrOp != otherNode->blrOp) return false; @@ -366,7 +366,7 @@ BoolExprNode* ComparativeBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (dsqlSpecialArg) { - ValueListNode* listNode = dsqlSpecialArg->as(); + ValueListNode* listNode = nodeAs(dsqlSpecialArg); if (listNode) { int listItemCount = 0; @@ -392,7 +392,7 @@ BoolExprNode* ComparativeBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) return resultNode->dsqlPass(dsqlScratch); } - SelectExprNode* selNode = dsqlSpecialArg->as(); + SelectExprNode* selNode = nodeAs(dsqlSpecialArg); if (selNode) { fb_assert(!(selNode->dsqlFlags & RecordSourceNode::DFLAG_SINGLETON)); @@ -495,7 +495,7 @@ bool ComparativeBoolNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) c if (!BoolExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const ComparativeBoolNode* o = other->as(); + const ComparativeBoolNode* o = nodeAs(other); fb_assert(o); return dsqlFlag == o->dsqlFlag && blrOp == o->blrOp; @@ -503,7 +503,7 @@ bool ComparativeBoolNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) c bool ComparativeBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const { - const ComparativeBoolNode* const otherNode = other->as(); + const ComparativeBoolNode* const otherNode = nodeAs(other); if (!otherNode || blrOp != otherNode->blrOp) return false; @@ -582,7 +582,7 @@ BoolExprNode* ComparativeBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) // If there is no top-level RSE present and patterns are not constant, unmark node as invariant // because it may be dependent on data or variables. if ((nodFlags & FLAG_INVARIANT) && - (!arg2->is() || (arg3 && !arg3->is()))) + (!nodeIs(arg2) || (arg3 && !nodeIs(arg3)))) { ExprNode* const* ctx_node; ExprNode* const* end; @@ -590,7 +590,7 @@ BoolExprNode* ComparativeBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) for (ctx_node = csb->csb_current_nodes.begin(), end = csb->csb_current_nodes.end(); ctx_node != end; ++ctx_node) { - if ((*ctx_node)->as()) + if (nodeAs(*ctx_node)) break; } @@ -614,7 +614,7 @@ void ComparativeBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) if (arg3) { - if ((keyNode = arg3->as()) && keyNode->aggregate) + if ((keyNode = nodeAs(arg3)) && keyNode->aggregate) ERR_post(Arg::Gds(isc_bad_dbkey)); dsc descriptor_c; @@ -627,8 +627,8 @@ void ComparativeBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) } } - if (((keyNode = arg1->as()) && keyNode->aggregate) || - ((keyNode = arg2->as()) && keyNode->aggregate)) + if (((keyNode = nodeAs(arg1)) && keyNode->aggregate) || + ((keyNode = nodeAs(arg2)) && keyNode->aggregate)) { ERR_post(Arg::Gds(isc_bad_dbkey)); } @@ -768,13 +768,13 @@ bool ComparativeBoolNode::execute(thread_db* tdbb, jrd_req* request) const case blr_leq: case blr_neq: case blr_between: - comparison = MOV_compare(desc[0], desc[1]); + comparison = MOV_compare(tdbb, desc[0], desc[1]); } // If we are checking equality of record_version // and same transaction updated the record, force equality. - const RecordKeyNode* recVersionNode = arg1->as(); + const RecordKeyNode* recVersionNode = nodeAs(arg1); if (recVersionNode && recVersionNode->blrOp == blr_record_version && force_equal) comparison = 0; @@ -806,7 +806,7 @@ bool ComparativeBoolNode::execute(thread_db* tdbb, jrd_req* request) const desc[1] = EVL_expr(tdbb, request, arg3); if (request->req_flags & req_null) return false; - return comparison >= 0 && MOV_compare(desc[0], desc[1]) <= 0; + return comparison >= 0 && MOV_compare(tdbb, desc[0], desc[1]) <= 0; case blr_containing: case blr_starting: @@ -849,7 +849,7 @@ bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, jrd_req* request, dsc* VaryStr<256> temp1; USHORT xtype1; - const USHORT l1 = MOV_get_string_ptr(desc1, &xtype1, &p1, &temp1, sizeof(temp1)); + const USHORT l1 = MOV_get_string_ptr(tdbb, desc1, &xtype1, &p1, &temp1, sizeof(temp1)); fb_assert(xtype1 == type1); @@ -915,7 +915,7 @@ bool ComparativeBoolNode::stringBoolean(thread_db* tdbb, jrd_req* request, dsc* break; } - escape_length = MOV_make_string(desc, type1, + escape_length = MOV_make_string(tdbb, desc, type1, reinterpret_cast(&escape_str), &temp3, sizeof(temp3)); if (!escape_length || charset->length(escape_length, escape_str, true) != 1) @@ -1124,7 +1124,7 @@ bool ComparativeBoolNode::stringFunction(thread_db* tdbb, jrd_req* request, return false; } - escape_length = MOV_make_string(desc, ttype, + escape_length = MOV_make_string(tdbb, desc, ttype, reinterpret_cast(&escape_str), &temp3, sizeof(temp3)); if (!escape_length || charset->length(escape_length, escape_str, true) != 1) @@ -1375,7 +1375,7 @@ BoolExprNode* MissingBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) void MissingBoolNode::pass2Boolean2(thread_db* tdbb, CompilerScratch* csb) { - RecordKeyNode* keyNode = arg->as(); + RecordKeyNode* keyNode = nodeAs(arg); if (keyNode && keyNode->aggregate) ERR_post(Arg::Gds(isc_bad_dbkey)); @@ -1448,7 +1448,7 @@ BoolExprNode* NotBoolNode::copy(thread_db* tdbb, NodeCopier& copier) const BoolExprNode* NotBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) { - RseBoolNode* rseBoolean = arg->as(); + RseBoolNode* rseBoolean = nodeAs(arg); if (rseBoolean) { @@ -1475,7 +1475,7 @@ bool NotBoolNode::execute(thread_db* tdbb, jrd_req* request) const // Get rid of redundant nested NOT predicates. BoolExprNode* NotBoolNode::process(DsqlCompilerScratch* dsqlScratch, bool invert) { - NotBoolNode* notArg = arg->as(); + NotBoolNode* notArg = nodeAs(arg); if (notArg) { @@ -1486,12 +1486,12 @@ BoolExprNode* NotBoolNode::process(DsqlCompilerScratch* dsqlScratch, bool invert if (!invert) return arg->dsqlPass(dsqlScratch); - ComparativeBoolNode* cmpArg = arg->as(); - BinaryBoolNode* binArg = arg->as(); + ComparativeBoolNode* cmpArg = nodeAs(arg); + BinaryBoolNode* binArg = nodeAs(arg); // Do not handle special case: NOT IN - if (cmpArg && (!cmpArg->dsqlSpecialArg || !cmpArg->dsqlSpecialArg->is())) + if (cmpArg && (!cmpArg->dsqlSpecialArg || !nodeIs(cmpArg->dsqlSpecialArg))) { // Invert the given boolean. switch (cmpArg->blrOp) @@ -1646,7 +1646,7 @@ BoolExprNode* RseBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) const DsqlContextStack::iterator base(*dsqlScratch->context); RseBoolNode* node = FB_NEW_POOL(getPool()) RseBoolNode(getPool(), blrOp, - PASS1_rse(dsqlScratch, dsqlRse->as(), false)); + PASS1_rse(dsqlScratch, nodeAs(dsqlRse), false)); // Finish off by cleaning up contexts dsqlScratch->context->clear(base); @@ -1657,7 +1657,7 @@ BoolExprNode* RseBoolNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) void RseBoolNode::genBlr(DsqlCompilerScratch* dsqlScratch) { dsqlScratch->appendUChar(blrOp); - GEN_rse(dsqlScratch, dsqlRse->as()); + GEN_rse(dsqlScratch, nodeAs(dsqlRse)); } bool RseBoolNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const @@ -1665,7 +1665,7 @@ bool RseBoolNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!BoolExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const RseBoolNode* o = other->as(); + const RseBoolNode* o = nodeAs(other); fb_assert(o); return blrOp == o->blrOp; @@ -1676,7 +1676,7 @@ bool RseBoolNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!BoolExprNode::sameAs(other, ignoreStreams)) return false; - const RseBoolNode* const otherNode = other->as(); + const RseBoolNode* const otherNode = nodeAs(other); fb_assert(otherNode); return blrOp == otherNode->blrOp; @@ -1730,7 +1730,7 @@ BoolExprNode* RseBoolNode::pass1(thread_db* tdbb, CompilerScratch* csb) BoolExprNode* boolean = rse->rse_boolean; if (boolean) { - BinaryBoolNode* const binaryNode = boolean->as(); + BinaryBoolNode* const binaryNode = nodeAs(boolean); if (binaryNode && binaryNode->blrOp == blr_and) boolean = binaryNode->arg2; @@ -1856,7 +1856,7 @@ BoolExprNode* RseBoolNode::convertNeqAllToNotAny(thread_db* tdbb, CompilerScratc if (!outerRse || outerRse->type != RseNode::TYPE || outerRse->rse_relations.getCount() != 1 || !outerRse->rse_boolean || - !(outerRseNeq = outerRse->rse_boolean->as()) || + !(outerRseNeq = nodeAs(outerRse->rse_boolean)) || outerRseNeq->blrOp != blr_neq) { return NULL; diff --git a/src/dsql/DdlNodes.epp b/src/dsql/DdlNodes.epp index 87d6a53476..a3fbff3880 100644 --- a/src/dsql/DdlNodes.epp +++ b/src/dsql/DdlNodes.epp @@ -393,26 +393,8 @@ void defineComputed(DsqlCompilerScratch* dsqlScratch, RelationSourceNode* relati field->subType = desc.dsc_sub_type; } - if (field && field->precision == 0 && field->scale != 0) - { - switch (field->dtype) - { - case dtype_short: - field->precision = 4; - break; - - case dtype_long: - field->precision = 9; - break; - - case dtype_int64: - field->precision = 18; - break; - - default: - fb_assert(!DTYPE_IS_EXACT(field->dtype)); - } - } + if (field) + field->setExactPrecision(); dsqlScratch->resetContextStack(); @@ -1043,9 +1025,9 @@ void DdlNode::storeGlobalField(thread_db* tdbb, jrd_tra* transaction, MetaName& ++ptr, ++position) { const ValueExprNode* element = *ptr++; - const SLONG lrange = element->as()->getSlong(); + const SLONG lrange = nodeAs(element)->getSlong(); element = *ptr; - const SLONG hrange = element->as()->getSlong(); + const SLONG hrange = nodeAs(element)->getSlong(); if (lrange >= hrange) { @@ -1565,7 +1547,7 @@ DdlNode* CreateAlterFunctionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) const NestConst* ptr = variables->statements.begin(); for (const NestConst* const end = variables->statements.end(); ptr != end; ++ptr) { - const DeclareVariableNode* varNode = (*ptr)->as(); + const DeclareVariableNode* varNode = nodeAs(*ptr); if (varNode) { @@ -2011,9 +1993,11 @@ void CreateAlterFunctionNode::storeArgument(thread_db* tdbb, DsqlCompilerScratch ARG.RDB$MECHANISM.NULL = TRUE; ARG.RDB$FIELD_TYPE.NULL = TRUE; ARG.RDB$FIELD_LENGTH.NULL = TRUE; + ARG.RDB$FIELD_SUB_TYPE.NULL = TRUE; ARG.RDB$FIELD_PRECISION.NULL = TRUE; ARG.RDB$FIELD_SCALE.NULL = TRUE; ARG.RDB$CHARACTER_SET_ID.NULL = TRUE; + ARG.RDB$CHARACTER_LENGTH.NULL = TRUE; ARG.RDB$COLLATION_ID.NULL = TRUE; ARG.RDB$ARGUMENT_MECHANISM.NULL = TRUE; @@ -2569,7 +2553,7 @@ DdlNode* CreateAlterProcedureNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) const NestConst* ptr = variables->statements.begin(); for (const NestConst* const end = variables->statements.end(); ptr != end; ++ptr) { - const DeclareVariableNode* varNode = (*ptr)->as(); + const DeclareVariableNode* varNode = nodeAs(*ptr); if (varNode) { @@ -3878,6 +3862,7 @@ void CreateCollationNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra { if (MET_get_char_coll_subtype_info(tdbb, INTL_CS_COLL_TO_TTYPE(forCharSetId, fromCollationId), &info) && + forCharSetId != CS_METADATA && info.specificAttributes.hasData()) { UCharBuffer temp; @@ -4475,6 +4460,8 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld) case blr_d_float: case blr_double: case blr_float: + case blr_dec64: + case blr_dec128: switch (newFld.dyn_dtype) { case blr_blob: @@ -4571,6 +4558,44 @@ void AlterDomainNode::checkUpdate(const dyn_fld& origFld, const dyn_fld& newFld) } break; + case blr_dec64: + switch (origFld.dyn_dtype) + { + case blr_short: + case blr_long: + case blr_float: + case blr_d_float: + case blr_double: + case blr_dec64: + break; + + default: + // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. + errorCode = isc_dyn_invalid_dtype_conversion; + break; + } + break; + + case blr_dec128: + switch (origFld.dyn_dtype) + { + case blr_short: + case blr_long: + case blr_int64: + case blr_float: + case blr_d_float: + case blr_double: + case blr_dec64: + case blr_dec128: + break; + + default: + // Cannot change datatype for column %s. Conversion from base type %s to base type %s is not supported. + errorCode = isc_dyn_invalid_dtype_conversion; + break; + } + break; + // If the original field is a numeric field and the new field is a character field, // is there enough space in the new field? case blr_text: @@ -6365,7 +6390,7 @@ bool RelationNode::defineDefault(thread_db* /*tdbb*/, DsqlCompilerScratch* dsqlS value.assign(dsqlScratch->getBlrData()); - return ExprNode::is(input); + return nodeIs(input); } // Make a constraint object from a legacy node. @@ -8641,22 +8666,21 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra ValueExprNode* nameNode = fieldNode; const char* aliasName = NULL; - while (nameNode->is() || nameNode->is() || - nameNode->is()) + while (nodeIs(nameNode) || nodeIs(nameNode) || nodeIs(nameNode)) { DsqlAliasNode* aliasNode; DsqlMapNode* mapNode; DerivedFieldNode* derivedField; - if ((aliasNode = nameNode->as())) + if ((aliasNode = nodeAs(nameNode))) { if (!aliasName) aliasName = aliasNode->name.c_str(); nameNode = aliasNode->value; } - else if ((mapNode = nameNode->as())) + else if ((mapNode = nodeAs(nameNode))) nameNode = mapNode->map->map_node; - else if ((derivedField = nameNode->as())) + else if ((derivedField = nodeAs(nameNode))) { if (!aliasName) aliasName = derivedField->name.c_str(); @@ -8665,7 +8689,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra } const dsql_fld* nameField = NULL; - const FieldNode* fieldNameNode = nameNode->as(); + const FieldNode* fieldNameNode = nodeAs(nameNode); if (fieldNameNode) nameField = fieldNameNode->dsqlField; @@ -8679,7 +8703,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra // Check if this is a field or an expression. - DsqlAliasNode* aliasNode = fieldNode->as(); + DsqlAliasNode* aliasNode = nodeAs(fieldNode); if (aliasNode) fieldNode = aliasNode->value; @@ -8687,7 +8711,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra dsql_fld* field = NULL; const dsql_ctx* context = NULL; - fieldNameNode = fieldNode->as(); + fieldNameNode = nodeAs(fieldNode); if (fieldNameNode) { @@ -8714,7 +8738,7 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra if (ptr) { if (ptr < end) - fieldStr = (*ptr)->as()->dsqlName.c_str(); + fieldStr = nodeAs(*ptr)->dsqlName.c_str(); else { // Generate an error when going out of this loop. @@ -8830,6 +8854,8 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra else newField.subType = desc.dsc_sub_type; + newField.setExactPrecision(); + if (relField) // modifying a view { AutoRequest request2; @@ -8925,11 +8951,11 @@ void CreateAlterViewNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScra Arg::Gds(isc_col_name_err)); } - RseNode* querySpec = selectExpr->querySpec->as(); + RseNode* querySpec = nodeAs(selectExpr->querySpec); fb_assert(querySpec); if (querySpec->dsqlFrom->items.getCount() != 1 || - !querySpec->dsqlFrom->items[0]->is()) + !nodeIs(querySpec->dsqlFrom->items[0])) { // Only one table allowed for VIEW WITH CHECK OPTION status_exception::raise( @@ -8997,10 +9023,10 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc dsqlScratch->resetContextStack(); - RseNode* querySpec = selectExpr->querySpec->as(); + RseNode* querySpec = nodeAs(selectExpr->querySpec); fb_assert(querySpec); - ProcedureSourceNode* sourceNode = querySpec->dsqlFrom->items[0]->as(); + ProcedureSourceNode* sourceNode = nodeAs(querySpec->dsqlFrom->items[0]); if (triggerType == PRE_MODIFY_TRIGGER) { @@ -9069,14 +9095,14 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc NestConst valueNod = *ptr2; DsqlAliasNode* aliasNode; - if ((aliasNode = fieldNod->as())) + if ((aliasNode = nodeAs(fieldNod))) fieldNod = aliasNode->value; - if ((aliasNode = valueNod->as())) + if ((aliasNode = nodeAs(valueNod))) valueNod = aliasNode->value; - FieldNode* fieldNode = fieldNod->as(); - FieldNode* valueNode = valueNod->as(); + FieldNode* fieldNode = nodeAs(fieldNod); + FieldNode* valueNode = nodeAs(valueNod); // Generate the actual comparisons. @@ -9135,10 +9161,10 @@ void CreateAlterViewNode::createCheckTrigger(thread_db* tdbb, DsqlCompilerScratc ValueExprNode* valueNode = *ptr; DsqlAliasNode* aliasNode; - if ((aliasNode = valueNode->as())) + if ((aliasNode = nodeAs(valueNode))) valueNode = aliasNode->value; - FieldNode* fieldNode = valueNode->as(); + FieldNode* fieldNode = nodeAs(valueNode); fb_assert(fieldNode); savedNames.add(field->fld_name); @@ -9577,7 +9603,7 @@ void CreateIndexNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, for (; ptr != end; ++ptr) { MetaName& column = definition.columns.add(); - column = (*ptr)->as()->dsqlName; + column = nodeAs(*ptr)->dsqlName; } } else if (computed) @@ -10613,7 +10639,7 @@ void MappingNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, jrd if (ddlTriggerAction > 0) executeDdlTrigger(tdbb, dsqlScratch, transaction, DTW_AFTER, ddlTriggerAction, name, NULL); - DFW_post_work(transaction, dfw_clear_cache, NULL, MAPPING_CACHE); + DFW_post_work(transaction, dfw_clear_cache, NULL, Mapping::MAPPING_CACHE); savePoint.release(); // everything is ok } @@ -11005,7 +11031,7 @@ void GrantRevokeNode::execute(thread_db* tdbb, DsqlCompilerScratch* dsqlScratch, } // Invalidate system privileges cache - DFW_post_work(transaction, dfw_clear_cache, NULL, SYSTEM_PRIVILEGES_CACHE); + DFW_post_work(transaction, dfw_clear_cache, NULL, Mapping::SYSTEM_PRIVILEGES_CACHE); } } @@ -11123,7 +11149,7 @@ void GrantRevokeNode::modifyPrivileges(thread_db* tdbb, jrd_tra* transaction, SS for (NestConst* ptr = fields->items.begin(); ptr != fields->items.end(); ++ptr) { grantRevoke(tdbb, transaction, object, user, privs0, - (*ptr)->as()->dsqlName, option); + nodeAs(*ptr)->dsqlName, option); } } else diff --git a/src/dsql/DsqlCompilerScratch.cpp b/src/dsql/DsqlCompilerScratch.cpp index 352a94fa86..7d6800e56f 100644 --- a/src/dsql/DsqlCompilerScratch.cpp +++ b/src/dsql/DsqlCompilerScratch.cpp @@ -278,7 +278,7 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT DeclareVariableNode* varNode; - if ((varNode = parameter->as())) + if ((varNode = nodeAs(parameter))) { dsql_fld* field = varNode->dsqlDef->type; const NestConst* rest = ptr; @@ -287,7 +287,7 @@ void DsqlCompilerScratch::putLocalVariables(CompoundStmtNode* parameters, USHORT { const DeclareVariableNode* varNode2; - if ((varNode2 = (*rest)->as())) + if ((varNode2 = nodeAs(*rest))) { const dsql_fld* rest_field = varNode2->dsqlDef->type; @@ -607,11 +607,11 @@ void DsqlCompilerScratch::checkUnusedCTEs() const SelectExprNode* DsqlCompilerScratch::pass1RecursiveCte(SelectExprNode* input) { RecordSourceNode* const query = input->querySpec; - UnionSourceNode* unionQuery = query->as(); + UnionSourceNode* unionQuery = nodeAs(query); if (!unionQuery) { - if (!pass1RseIsRecursive(query->as())) + if (!pass1RseIsRecursive(nodeAs(query))) return input; ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << @@ -640,7 +640,7 @@ SelectExprNode* DsqlCompilerScratch::pass1RecursiveCte(SelectExprNode* input) if (iter == unionQuery->dsqlClauses->items.begin()) { - unionQuery = clause->as(); + unionQuery = nodeAs(clause); if (unionQuery) { @@ -654,7 +654,7 @@ SelectExprNode* DsqlCompilerScratch::pass1RecursiveCte(SelectExprNode* input) } } - RseNode* const rse = clause->as(); + RseNode* const rse = nodeAs(clause); fb_assert(rse); RseNode* const newRse = pass1RseIsRecursive(rse); @@ -815,7 +815,7 @@ RseNode* DsqlCompilerScratch::pass1RseIsRecursive(RseNode* input) { *prev++ = *pDstTable = *pSrcTable; - RseNode* rseNode = (*pDstTable)->as(); + RseNode* rseNode = nodeAs(*pDstTable); if (rseNode) { @@ -841,7 +841,7 @@ RseNode* DsqlCompilerScratch::pass1RseIsRecursive(RseNode* input) result->dsqlWhere = PASS1_compose(result->dsqlWhere, joinBool, blr_and); } } - else if ((*pDstTable)->is() || (*pDstTable)->is()) + else if (nodeIs(*pDstTable) || nodeIs(*pDstTable)) { if (pass1RelProcIsRecursive(*pDstTable)) { @@ -858,9 +858,7 @@ RseNode* DsqlCompilerScratch::pass1RseIsRecursive(RseNode* input) } } else - { - fb_assert((*pDstTable)->is()); - } + fb_assert(nodeIs(*pDstTable)); } if (found) @@ -877,12 +875,12 @@ bool DsqlCompilerScratch::pass1RelProcIsRecursive(RecordSourceNode* input) ProcedureSourceNode* procNode; RelationSourceNode* relNode; - if ((procNode = input->as())) + if ((procNode = nodeAs(input))) { relName = procNode->dsqlName.identifier; relAlias = procNode->alias; } - else if ((relNode = input->as())) + else if ((relNode = nodeAs(input))) { relName = relNode->dsqlName; relAlias = relNode->alias; @@ -906,7 +904,7 @@ bool DsqlCompilerScratch::pass1RelProcIsRecursive(RecordSourceNode* input) // outer join or more than one recursive reference is found BoolExprNode* DsqlCompilerScratch::pass1JoinIsRecursive(RecordSourceNode*& input) { - RseNode* inputRse = input->as(); + RseNode* inputRse = nodeAs(input); fb_assert(inputRse); const UCHAR joinType = inputRse->rse_jointype; @@ -917,7 +915,7 @@ BoolExprNode* DsqlCompilerScratch::pass1JoinIsRecursive(RecordSourceNode*& input NestConst* joinTable = &inputRse->dsqlFrom->items[0]; RseNode* joinRse; - if ((joinRse = (*joinTable)->as()) && joinRse->dsqlExplicitJoin) + if ((joinRse = nodeAs(*joinTable)) && joinRse->dsqlExplicitJoin) { leftBool = pass1JoinIsRecursive(*joinTable->getAddress()); leftRecursive = (leftBool != NULL); @@ -943,7 +941,7 @@ BoolExprNode* DsqlCompilerScratch::pass1JoinIsRecursive(RecordSourceNode*& input joinTable = &inputRse->dsqlFrom->items[1]; - if ((joinRse = (*joinTable)->as()) && joinRse->dsqlExplicitJoin) + if ((joinRse = nodeAs(*joinTable)) && joinRse->dsqlExplicitJoin) { rightBool = pass1JoinIsRecursive(*joinTable->getAddress()); rightRecursive = (rightBool != NULL); diff --git a/src/dsql/ExprNodes.cpp b/src/dsql/ExprNodes.cpp index 7fe34cf928..b182d78b0f 100644 --- a/src/dsql/ExprNodes.cpp +++ b/src/dsql/ExprNodes.cpp @@ -48,6 +48,7 @@ #include "../jrd/mov_proto.h" #include "../jrd/pag_proto.h" #include "../jrd/par_proto.h" +#include "../jrd/cvt2_proto.h" #include "../dsql/ddl_proto.h" #include "../dsql/errd_proto.h" #include "../dsql/gen_proto.h" @@ -147,7 +148,7 @@ namespace for (ExprNode** node = csb->csb_current_nodes.end() - 1; node >= csb->csb_current_nodes.begin(); --node) { - RseNode* const rseNode = (*node)->as(); + RseNode* const rseNode = nodeAs(*node); if (rseNode) { @@ -194,7 +195,7 @@ void NodeRef::pass2(thread_db* tdbb, CompilerScratch* csb) { if (csb->csb_current_nodes.hasData()) { - RseNode* topRseNode = csb->csb_current_nodes[0]->as(); + RseNode* topRseNode = nodeAs(csb->csb_current_nodes[0]); fb_assert(topRseNode); if (!topRseNode->rse_invariants) @@ -485,7 +486,7 @@ void ArithmeticNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) desc2.setNull(); } - if (arg1->is() && arg2->is()) + if (nodeIs(arg1) && nodeIs(arg2)) { // NULL + NULL = NULL of INT desc->makeLong(0); @@ -647,6 +648,14 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2) desc->dsc_length = sizeof(double); break; + case dtype_dec64: + case dtype_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(Decimal128); + break; + default: desc->dsc_dtype = dtype_long; desc->dsc_sub_type = 0; @@ -670,6 +679,13 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2) switch (dtype) { + case dtype_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(Decimal128); + break; + case dtype_double: desc->dsc_dtype = dtype_double; desc->dsc_sub_type = 0; @@ -709,6 +725,15 @@ void ArithmeticNode::makeDialect1(dsc* desc, dsc& desc1, dsc& desc2) dtype = MAX(dtype1, dtype2); + if (DTYPE_IS_DECFLOAT(dtype)) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; + break; + } + if (!DTYPE_IS_NUMERIC(dtype)) { ERRD_post(Arg::Gds(isc_expression_eval_err) << @@ -755,6 +780,8 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) // arithmetic, but returns a if (DTYPE_IS_EXACT(dtype1) && DTYPE_IS_EXACT(dtype2)) dtype = dtype_int64; + else if (desc1.isDecOrInt() && desc2.isDecOrInt()) + dtype = dtype_dec128; else if (DTYPE_IS_NUMERIC(dtype1) && DTYPE_IS_NUMERIC(dtype2)) { fb_assert(DTYPE_IS_APPROX(dtype1) || DTYPE_IS_APPROX(dtype2)); @@ -771,7 +798,7 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) if (dtype_int64 == dtype2) dtype2 = dtype_double; - dtype = MAX(dtype1, dtype2); + dtype = CVT2_compare_priority[dtype1] > CVT2_compare_priority[dtype2] ? dtype1 : dtype2; } desc->dsc_flags = (desc1.dsc_flags | desc2.dsc_flags) & DSC_nullable; @@ -875,6 +902,14 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) desc->dsc_length = sizeof(double); break; + case dtype_dec64: + case dtype_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(Decimal128); + break; + case dtype_short: case dtype_long: case dtype_int64: @@ -920,6 +955,13 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) switch (dtype) { + case dtype_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_sub_type = 0; + desc->dsc_scale = 0; + desc->dsc_length = sizeof(Decimal128); + break; + case dtype_double: desc->dsc_dtype = dtype_double; desc->dsc_sub_type = 0; @@ -973,6 +1015,11 @@ void ArithmeticNode::makeDialect3(dsc* desc, dsc& desc1, dsc& desc2) desc->dsc_scale = 0; break; + case dtype_dec128: + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + break; + default: ERRD_post(Arg::Gds(isc_expression_eval_err) << Arg::Gds(isc_dsql_invalid_type_div_dial3)); @@ -1168,6 +1215,16 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, desc->dsc_flags = 0; return; + case dtype_dec64: + case dtype_dec128: + nodFlags |= FLAG_DECFLOAT; + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + break; + case dtype_unknown: desc->dsc_dtype = dtype_unknown; desc->dsc_length = 0; @@ -1210,6 +1267,15 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, desc->dsc_flags = 0; return; + case dtype_dec128: + nodFlags |= FLAG_DECFLOAT; + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + break; + case dtype_unknown: desc->dsc_dtype = dtype_unknown; desc->dsc_length = 0; @@ -1230,6 +1296,18 @@ void ArithmeticNode::getDescDialect1(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, break; case blr_divide: + /*** + if (desc1.isDecOrInt() && desc2.isDecOrInt()) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + } + ***/ + // for compatibility with older versions of the product, we accept // text types for division in blr_version4 (dialect <= 1) only if (!(DTYPE_IS_NUMERIC(desc1.dsc_dtype) || DTYPE_IS_TEXT(desc1.dsc_dtype))) @@ -1270,7 +1348,7 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, USHORT dtype1 = desc1.dsc_dtype; USHORT dtype2 = desc2.dsc_dtype; - // In Dialect 2 or 3, strings can never partipate in addition / sub + // In Dialect 2 or 3, strings can never participate in addition / sub // (use a specific cast instead) if (DTYPE_IS_TEXT(dtype1) || DTYPE_IS_TEXT(dtype2)) ERR_post(Arg::Gds(isc_expression_eval_err)); @@ -1281,6 +1359,8 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, if (DTYPE_IS_EXACT(desc1.dsc_dtype) && DTYPE_IS_EXACT(desc2.dsc_dtype)) dtype = dtype_int64; + else if (desc1.isDecOrInt() && desc2.isDecOrInt()) + dtype = dtype_dec128; else if (DTYPE_IS_NUMERIC(desc1.dsc_dtype) && DTYPE_IS_NUMERIC(desc2.dsc_dtype)) dtype = dtype_double; else @@ -1411,6 +1491,16 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, desc->dsc_flags = 0; return; + case dtype_dec64: + case dtype_dec128: + nodFlags |= FLAG_DECFLOAT; + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + case dtype_short: case dtype_long: case dtype_int64: @@ -1460,6 +1550,15 @@ void ArithmeticNode::getDescDialect3(thread_db* /*tdbb*/, dsc* desc, dsc& desc1, desc->dsc_flags = 0; return; + case dtype_dec128: + nodFlags |= FLAG_DECFLOAT; + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + return; + case dtype_int64: desc->dsc_dtype = dtype_int64; desc->dsc_length = sizeof(SINT64); @@ -1509,7 +1608,7 @@ bool ArithmeticNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const ArithmeticNode* o = other->as(); + const ArithmeticNode* o = nodeAs(other); fb_assert(o); return dialect1 == o->dialect1 && blrOp == o->blrOp; @@ -1517,7 +1616,7 @@ bool ArithmeticNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const bool ArithmeticNode::sameAs(const ExprNode* other, bool ignoreStreams) const { - const ArithmeticNode* const otherNode = other->as(); + const ArithmeticNode* const otherNode = nodeAs(other); if (!otherNode || blrOp != otherNode->blrOp || dialect1 != otherNode->dialect1) return false; @@ -1590,7 +1689,7 @@ dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const case blr_divide: { - const double divisor = MOV_get_double(desc2); + const double divisor = MOV_get_double(tdbb, desc2); if (divisor == 0) { @@ -1598,7 +1697,7 @@ dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const Arg::Gds(isc_exception_float_divide_by_zero)); } - impure->vlu_misc.vlu_double = MOV_get_double(desc1) / divisor; + impure->vlu_misc.vlu_double = MOV_get_double(tdbb, desc1) / divisor; if (isinf(impure->vlu_misc.vlu_double)) { @@ -1633,7 +1732,7 @@ dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const } } - BUGCHECK(232); // msg 232 EVL_expr: invalid operation + SOFT_BUGCHECK(232); // msg 232 EVL_expr: invalid operation return NULL; } @@ -1641,14 +1740,15 @@ dsc* ArithmeticNode::execute(thread_db* tdbb, jrd_req* request) const // This function can be removed when dialect-3 becomes the lowest supported dialect. (Version 7.0?) dsc* ArithmeticNode::add(const dsc* desc, impure_value* value, const ValueExprNode* node, const UCHAR blrOp) { - const ArithmeticNode* arithmeticNode = node->as(); + const ArithmeticNode* arithmeticNode = nodeAs(node); + thread_db* tdbb = JRD_get_thread_data(); #ifdef DEV_BUILD - const SubQueryNode* subQueryNode = node->as(); + const SubQueryNode* subQueryNode = nodeAs(node); fb_assert( (arithmeticNode && arithmeticNode->dialect1 && (arithmeticNode->blrOp == blr_add || arithmeticNode->blrOp == blr_subtract)) || - node->is() || + nodeIs(node) || (subQueryNode && (subQueryNode->blrOp == blr_total || subQueryNode->blrOp == blr_average))); #endif @@ -1662,12 +1762,31 @@ dsc* ArithmeticNode::add(const dsc* desc, impure_value* value, const ValueExprNo return arithmeticNode->addDateTime(desc, value); } + // Handle decimal arithmetic + + if (node->nodFlags & FLAG_DECFLOAT) + { + const Decimal128 d1 = MOV_get_dec128(tdbb, desc); + const Decimal128 d2 = MOV_get_dec128(tdbb, &value->vlu_desc); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + value->vlu_misc.vlu_dec128 = (blrOp == blr_subtract) ? d2.sub(decSt, d1) : d1.add(decSt, d2); + + result->dsc_dtype = dtype_dec128; + result->dsc_length = sizeof(Decimal128); + result->dsc_scale = 0; + result->dsc_sub_type = 0; + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec128; + + return result; + } + // Handle floating arithmetic if (node->nodFlags & FLAG_DOUBLE) { - const double d1 = MOV_get_double(desc); - const double d2 = MOV_get_double(&value->vlu_desc); + const double d1 = MOV_get_double(tdbb, desc); + const double d2 = MOV_get_double(tdbb, &value->vlu_desc); value->vlu_misc.vlu_double = (blrOp == blr_subtract) ? d2 - d1 : d1 + d2; @@ -1688,8 +1807,8 @@ dsc* ArithmeticNode::add(const dsc* desc, impure_value* value, const ValueExprNo // CVC: Maybe we should upgrade the sum to double if it doesn't fit? // This is what was done for multiplicaton in dialect 1. - const SLONG l1 = MOV_get_long(desc, node->nodScale); - const SINT64 l2 = MOV_get_long(&value->vlu_desc, node->nodScale); + const SLONG l1 = MOV_get_long(tdbb, desc, node->nodScale); + const SINT64 l2 = MOV_get_long(tdbb, &value->vlu_desc, node->nodScale); const SINT64 rc = (blrOp == blr_subtract) ? l2 - l1 : l2 + l1; if (rc < MIN_SLONG || rc > MAX_SLONG) @@ -1704,12 +1823,12 @@ dsc* ArithmeticNode::add(const dsc* desc, impure_value* value, const ValueExprNo // the blr_add, blr_subtract, and blr_agg_total verbs following a blr_version5. dsc* ArithmeticNode::add2(const dsc* desc, impure_value* value, const ValueExprNode* node, const UCHAR blrOp) { - const ArithmeticNode* arithmeticNode = node->as(); + const ArithmeticNode* arithmeticNode = nodeAs(node); fb_assert( (arithmeticNode && !arithmeticNode->dialect1 && (arithmeticNode->blrOp == blr_add || arithmeticNode->blrOp == blr_subtract)) || - node->is()); + nodeIs(node)); dsc* result = &value->vlu_desc; @@ -1721,12 +1840,33 @@ dsc* ArithmeticNode::add2(const dsc* desc, impure_value* value, const ValueExprN return arithmeticNode->addDateTime(desc, value); } + thread_db* tdbb = JRD_get_thread_data(); + + // Handle decimal arithmetic + + if (node->nodFlags & FLAG_DECFLOAT) + { + const Decimal128 d1 = MOV_get_dec128(tdbb, desc); + const Decimal128 d2 = MOV_get_dec128(tdbb, &value->vlu_desc); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + value->vlu_misc.vlu_dec128 = (blrOp == blr_subtract) ? d2.sub(decSt, d1) : d1.add(decSt, d2); + + result->dsc_dtype = dtype_dec128; + result->dsc_length = sizeof(Decimal128); + result->dsc_scale = 0; + result->dsc_sub_type = 0; + result->dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec128; + + return result; + } + // Handle floating arithmetic if (node->nodFlags & FLAG_DOUBLE) { - const double d1 = MOV_get_double(desc); - const double d2 = MOV_get_double(&value->vlu_desc); + const double d1 = MOV_get_double(tdbb, desc); + const double d2 = MOV_get_double(tdbb, &value->vlu_desc); value->vlu_misc.vlu_double = (blrOp == blr_subtract) ? d2 - d1 : d1 + d2; @@ -1744,8 +1884,8 @@ dsc* ArithmeticNode::add2(const dsc* desc, impure_value* value, const ValueExprN // Everything else defaults to int64 - SINT64 i1 = MOV_get_int64(desc, node->nodScale); - const SINT64 i2 = MOV_get_int64(&value->vlu_desc, node->nodScale); + SINT64 i1 = MOV_get_int64(tdbb, desc, node->nodScale); + const SINT64 i2 = MOV_get_int64(tdbb, &value->vlu_desc, node->nodScale); result->dsc_dtype = dtype_int64; result->dsc_length = sizeof(SINT64); @@ -1785,14 +1925,34 @@ dsc* ArithmeticNode::add2(const dsc* desc, impure_value* value, const ValueExprN // This function can be removed when dialect-3 becomes the lowest supported dialect. (Version 7.0?) dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const { + thread_db* tdbb = JRD_get_thread_data(); DEV_BLKCHK(node, type_nod); + // Handle decimal arithmetic + + if (nodFlags & FLAG_DECFLOAT) + { + const Decimal128 d1 = MOV_get_dec128(tdbb, desc); + const Decimal128 d2 = MOV_get_dec128(tdbb, &value->vlu_desc); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + value->vlu_misc.vlu_dec128 = d1.mul(decSt, d2); + + value->vlu_desc.dsc_dtype = dtype_dec128; + value->vlu_desc.dsc_length = sizeof(Decimal128); + value->vlu_desc.dsc_scale = 0; + value->vlu_desc.dsc_sub_type = 0; + value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec128; + + return &value->vlu_desc; + } + // Handle floating arithmetic if (nodFlags & FLAG_DOUBLE) { - const double d1 = MOV_get_double(desc); - const double d2 = MOV_get_double(&value->vlu_desc); + const double d1 = MOV_get_double(tdbb, desc); + const double d2 = MOV_get_double(tdbb, &value->vlu_desc); value->vlu_misc.vlu_double = d1 * d2; if (isinf(value->vlu_misc.vlu_double)) @@ -1820,8 +1980,8 @@ dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const // SLONG l1, l2; //{ const SSHORT scale = NUMERIC_SCALE(value->vlu_desc); - const SINT64 i1 = MOV_get_long(desc, nodScale - scale); - const SINT64 i2 = MOV_get_long(&value->vlu_desc, scale); + const SINT64 i1 = MOV_get_long(tdbb, desc, nodScale - scale); + const SINT64 i2 = MOV_get_long(tdbb, &value->vlu_desc, scale); value->vlu_desc.dsc_dtype = dtype_long; value->vlu_desc.dsc_length = sizeof(SLONG); value->vlu_desc.dsc_scale = nodScale; @@ -1833,7 +1993,7 @@ dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_int64; value->vlu_desc.dsc_dtype = dtype_int64; value->vlu_desc.dsc_length = sizeof(SINT64); - value->vlu_misc.vlu_double = MOV_get_double(&value->vlu_desc); + value->vlu_misc.vlu_double = MOV_get_double(tdbb, &value->vlu_desc); /* This is the Borland solution instead of the five lines above. d1 = MOV_get_double (desc); d2 = MOV_get_double (&value->vlu_desc); @@ -1859,14 +2019,34 @@ dsc* ArithmeticNode::multiply(const dsc* desc, impure_value* value) const // Multiply two numbers, with dialect-3 semantics, implementing blr_version5 ... blr_multiply. dsc* ArithmeticNode::multiply2(const dsc* desc, impure_value* value) const { + thread_db* tdbb = JRD_get_thread_data(); DEV_BLKCHK(node, type_nod); + // Handle decimal arithmetic + + if (nodFlags & FLAG_DECFLOAT) + { + const Decimal128 d1 = MOV_get_dec128(tdbb, desc); + const Decimal128 d2 = MOV_get_dec128(tdbb, &value->vlu_desc); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + value->vlu_misc.vlu_dec128 = d1.mul(decSt, d2); + + value->vlu_desc.dsc_dtype = dtype_dec128; + value->vlu_desc.dsc_length = sizeof(Decimal128); + value->vlu_desc.dsc_scale = 0; + value->vlu_desc.dsc_sub_type = 0; + value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec128; + + return &value->vlu_desc; + } + // Handle floating arithmetic if (nodFlags & FLAG_DOUBLE) { - const double d1 = MOV_get_double(desc); - const double d2 = MOV_get_double(&value->vlu_desc); + const double d1 = MOV_get_double(tdbb, desc); + const double d2 = MOV_get_double(tdbb, &value->vlu_desc); value->vlu_misc.vlu_double = d1 * d2; if (isinf(value->vlu_misc.vlu_double)) @@ -1886,8 +2066,8 @@ dsc* ArithmeticNode::multiply2(const dsc* desc, impure_value* value) const // Everything else defaults to int64 const SSHORT scale = NUMERIC_SCALE(value->vlu_desc); - const SINT64 i1 = MOV_get_int64(desc, nodScale - scale); - const SINT64 i2 = MOV_get_int64(&value->vlu_desc, scale); + const SINT64 i1 = MOV_get_int64(tdbb, desc, nodScale - scale); + const SINT64 i2 = MOV_get_int64(tdbb, &value->vlu_desc, scale); /* We need to report an overflow if @@ -1935,19 +2115,39 @@ dsc* ArithmeticNode::multiply2(const dsc* desc, impure_value* value) const // blr_version5 ... blr_average .... dsc* ArithmeticNode::divide2(const dsc* desc, impure_value* value) const { + thread_db* tdbb = JRD_get_thread_data(); DEV_BLKCHK(node, type_nod); + // Handle decimal arithmetic + + if (nodFlags & FLAG_DECFLOAT) + { + const Decimal128 d1 = MOV_get_dec128(tdbb, desc); + const Decimal128 d2 = MOV_get_dec128(tdbb, &value->vlu_desc); + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + value->vlu_misc.vlu_dec128 = d2.div(decSt, d1); + + value->vlu_desc.dsc_dtype = dtype_dec128; + value->vlu_desc.dsc_length = sizeof(Decimal128); + value->vlu_desc.dsc_scale = 0; + value->vlu_desc.dsc_sub_type = 0; + value->vlu_desc.dsc_address = (UCHAR*) &value->vlu_misc.vlu_dec128; + + return &value->vlu_desc; + } + // Handle floating arithmetic if (nodFlags & FLAG_DOUBLE) { - const double d2 = MOV_get_double(desc); + const double d2 = MOV_get_double(tdbb, desc); if (d2 == 0.0) { ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_divide_by_zero)); } - const double d1 = MOV_get_double(&value->vlu_desc); + const double d1 = MOV_get_double(tdbb, &value->vlu_desc); value->vlu_misc.vlu_double = d1 / d2; if (isinf(value->vlu_misc.vlu_double)) { @@ -1998,14 +2198,14 @@ dsc* ArithmeticNode::divide2(const dsc* desc, impure_value* value) const * Who'da thunk that 9th-grade algebra would prove so useful. * -- Chris Jewell, December 1998 */ - SINT64 i2 = MOV_get_int64(desc, desc->dsc_scale); + SINT64 i2 = MOV_get_int64(tdbb, desc, desc->dsc_scale); if (i2 == 0) { ERR_post(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_integer_divide_by_zero)); } - SINT64 i1 = MOV_get_int64(&value->vlu_desc, nodScale - desc->dsc_scale); + SINT64 i1 = MOV_get_int64(tdbb, &value->vlu_desc, nodScale - desc->dsc_scale); // MIN_SINT64 / -1 = (MAX_SINT64 + 1), which overflows in SINT64. if ((i1 == MIN_SINT64) && (i2 == -1)) @@ -2141,6 +2341,7 @@ dsc* ArithmeticNode::addSqlDate(const dsc* desc, impure_value* value) const fb_assert(blrOp == blr_add || blrOp == blr_subtract); dsc* result = &value->vlu_desc; + thread_db* tdbb = JRD_get_thread_data(); fb_assert(value->vlu_desc.dsc_dtype == dtype_sql_date || desc->dsc_dtype == dtype_sql_date); @@ -2153,7 +2354,7 @@ dsc* ArithmeticNode::addSqlDate(const dsc* desc, impure_value* value) const op1_is_date = true; } else - d1 = MOV_get_int64(&value->vlu_desc, 0); + d1 = MOV_get_int64(tdbb, &value->vlu_desc, 0); SINT64 d2; // Coerce operand2 to a count of days @@ -2164,7 +2365,7 @@ dsc* ArithmeticNode::addSqlDate(const dsc* desc, impure_value* value) const op2_is_date = true; } else - d2 = MOV_get_int64(desc, 0); + d2 = MOV_get_int64(tdbb, desc, 0); if (blrOp == blr_subtract && op1_is_date && op2_is_date) { @@ -2210,6 +2411,7 @@ dsc* ArithmeticNode::addSqlTime(const dsc* desc, impure_value* value) const fb_assert(blrOp == blr_add || blrOp == blr_subtract); dsc* result = &value->vlu_desc; + thread_db* tdbb = JRD_get_thread_data(); fb_assert(value->vlu_desc.dsc_dtype == dtype_sql_time || desc->dsc_dtype == dtype_sql_time); @@ -2223,7 +2425,7 @@ dsc* ArithmeticNode::addSqlTime(const dsc* desc, impure_value* value) const fb_assert(d1 >= 0 && d1 < ISC_TICKS_PER_DAY); } else - d1 = MOV_get_int64(&value->vlu_desc, ISC_TIME_SECONDS_PRECISION_SCALE); + d1 = MOV_get_int64(tdbb, &value->vlu_desc, ISC_TIME_SECONDS_PRECISION_SCALE); SINT64 d2; // Coerce operand2 to a count of seconds @@ -2235,7 +2437,7 @@ dsc* ArithmeticNode::addSqlTime(const dsc* desc, impure_value* value) const fb_assert(d2 >= 0 && d2 < ISC_TICKS_PER_DAY); } else - d2 = MOV_get_int64(desc, ISC_TIME_SECONDS_PRECISION_SCALE); + d2 = MOV_get_int64(tdbb, desc, ISC_TIME_SECONDS_PRECISION_SCALE); if (blrOp == blr_subtract && op1_is_time && op2_is_time) { @@ -2702,7 +2904,7 @@ bool CastNode::setParameterType(DsqlCompilerScratch* /*dsqlScratch*/, { // ASF: Attention: CastNode::dsqlPass calls us with NULL node. - ParameterNode* paramNode = source->as(); + ParameterNode* paramNode = nodeAs(source); if (paramNode) { @@ -2780,7 +2982,7 @@ bool CastNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const CastNode* o = other->as(); + const CastNode* o = nodeAs(other); fb_assert(o); return dsqlField == o->dsqlField; @@ -2791,7 +2993,7 @@ bool CastNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const CastNode* const otherNode = other->as(); + const CastNode* const otherNode = nodeAs(other); fb_assert(otherNode); return DSC_EQUIV(&castDesc, &otherNode->castDesc, true); @@ -3967,13 +4169,13 @@ bool DecodeNode::setParameterType(DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool /*forceVarChar*/) { // Check if there is a parameter in the test/conditions. - bool setParameters = test->is(); + bool setParameters = nodeIs(test); if (!setParameters) { for (auto& condition : conditions->items) { - if (condition->is()) + if (nodeIs(condition)) { setParameters = true; break; @@ -4101,7 +4303,7 @@ dsc* DecodeNode::execute(thread_db* tdbb, jrd_req* request) const { dsc* desc = EVL_expr(tdbb, request, condition); - if (desc && !(request->req_flags & req_null) && MOV_compare(testDesc, desc) == 0) + if (desc && !(request->req_flags & req_null) && MOV_compare(tdbb, testDesc, desc) == 0) return EVL_expr(tdbb, request, *valuesPtr); ++valuesPtr; @@ -4250,7 +4452,7 @@ bool DefaultNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const DefaultNode* o = other->as(); + const DefaultNode* o = nodeAs(other); fb_assert(o); return relationName == o->relationName && fieldName == o->fieldName; @@ -4568,7 +4770,7 @@ ValueExprNode* ExtractNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) case blr_extract_weekday: case blr_extract_yearday: case blr_extract_week: - if (!ExprNode::is(sub1) && + if (!nodeIs(sub1) && sub1->nodDesc.dsc_dtype != dtype_sql_date && sub1->nodDesc.dsc_dtype != dtype_timestamp) { @@ -4581,7 +4783,7 @@ ValueExprNode* ExtractNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) case blr_extract_minute: case blr_extract_second: case blr_extract_millisecond: - if (!ExprNode::is(sub1) && + if (!nodeIs(sub1) && sub1->nodDesc.dsc_dtype != dtype_sql_time && sub1->nodDesc.dsc_dtype != dtype_timestamp) { @@ -4671,7 +4873,7 @@ bool ExtractNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const ExtractNode* o = other->as(); + const ExtractNode* o = nodeAs(other); fb_assert(o); return blrSubOp == o->blrSubOp; @@ -4682,7 +4884,7 @@ bool ExtractNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const ExtractNode* const otherNode = other->as(); + const ExtractNode* const otherNode = nodeAs(other); fb_assert(otherNode); return blrSubOp == otherNode->blrSubOp; @@ -5333,7 +5535,7 @@ ValueExprNode* FieldNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, Rec for (auto& rseItem : rseItems->items) { - DerivedFieldNode* selectItem = rseItem->as(); + DerivedFieldNode* selectItem = nodeAs(rseItem); // select-item should always be a alias! if (selectItem) @@ -5614,7 +5816,7 @@ bool FieldNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const FieldNode* o = other->as(); + const FieldNode* o = nodeAs(other); fb_assert(o); if (dsqlField != o->dsqlField || dsqlContext != o->dsqlContext) @@ -5631,7 +5833,7 @@ bool FieldNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const FieldNode* const otherNode = other->as(); + const FieldNode* const otherNode = nodeAs(other); fb_assert(otherNode); return fieldId == otherNode->fieldId && @@ -6173,7 +6375,7 @@ bool GenIdNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const GenIdNode* o = other->as(); + const GenIdNode* o = nodeAs(other); fb_assert(o); // I'm not sure if I should include "implicit" in the comparison, but it means different BLR code @@ -6187,7 +6389,7 @@ bool GenIdNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const GenIdNode* const otherNode = other->as(); + const GenIdNode* const otherNode = nodeAs(other); fb_assert(otherNode); // I'm not sure if I should include "implicit" in the comparison, but it means different BLR code @@ -6234,7 +6436,7 @@ dsc* GenIdNode::execute(thread_db* tdbb, jrd_req* request) const if (request->req_flags & req_null) return NULL; - change = MOV_get_int64(value, 0); + change = MOV_get_int64(tdbb, value, 0); } if (sysGen && change != 0) @@ -6289,7 +6491,7 @@ DmlNode* InternalInfoNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScra node->arg = PAR_parse_value(tdbb, csb); - LiteralNode* literal = node->arg->as(); + LiteralNode* literal = nodeAs(node->arg); if (!literal || literal->litDesc.dsc_dtype != dtype_long) { @@ -6311,7 +6513,7 @@ string InternalInfoNode::internalPrint(NodePrinter& printer) const void InternalInfoNode::setParameterName(dsql_par* parameter) const { - SLONG infoType = arg->as()->getSlong(); + SLONG infoType = nodeAs(arg)->getSlong(); parameter->par_name = parameter->par_alias = INFO_TYPE_ATTRIBUTES[infoType].alias; } @@ -6323,7 +6525,7 @@ void InternalInfoNode::genBlr(DsqlCompilerScratch* dsqlScratch) void InternalInfoNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) { - const InfoType infoType = static_cast(arg->as()->getSlong()); + const InfoType infoType = static_cast(nodeAs(arg)->getSlong()); switch (infoType) { @@ -6358,7 +6560,7 @@ void InternalInfoNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) void InternalInfoNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { - fb_assert(arg->is()); + fb_assert(nodeIs(arg)); dsc argDesc; arg->getDesc(tdbb, csb, &argDesc); @@ -6499,7 +6701,7 @@ dsc* InternalInfoNode::execute(thread_db* tdbb, jrd_req* request) const result32 = request->req_trigger_action; break; default: - BUGCHECK(232); // msg 232 EVL_expr: invalid operation + SOFT_BUGCHECK(232); // msg 232 EVL_expr: invalid operation } dsc desc; @@ -6515,7 +6717,7 @@ dsc* InternalInfoNode::execute(thread_db* tdbb, jrd_req* request) const ValueExprNode* InternalInfoNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { - SLONG infoType = arg->as()->getSlong(); + SLONG infoType = nodeAs(arg)->getSlong(); const InfoAttr& attr = INFO_TYPE_ATTRIBUTES[infoType]; if (attr.mask && !(dsqlScratch->flags & attr.mask)) @@ -6549,11 +6751,14 @@ DmlNode* LiteralNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* PAR_desc(tdbb, csb, &node->litDesc); - UCHAR* p = FB_NEW_POOL(csb->csb_pool) UCHAR[node->litDesc.dsc_length]; + USHORT l = node->litDesc.dsc_length; + USHORT dataLen = l; + if (node->litDesc.dsc_dtype == dtype_double && dataLen < sizeof(Decimal128)) + dataLen = sizeof(Decimal128); + UCHAR* p = FB_NEW_POOL(csb->csb_pool) UCHAR[dataLen]; node->litDesc.dsc_address = p; node->litDesc.dsc_flags = 0; const UCHAR* q = csb->csb_blr_reader.getPos(); - USHORT l = node->litDesc.dsc_length; switch (node->litDesc.dsc_dtype) { @@ -6583,6 +6788,7 @@ DmlNode* LiteralNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* break; case dtype_double: + case dtype_dec128: { SSHORT scale; UCHAR dtype; @@ -6593,7 +6799,7 @@ DmlNode* LiteralNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* l = csb->csb_blr_reader.getWord(); q = csb->csb_blr_reader.getPos(); - dtype = CVT_get_numeric(q, l, &scale, (double*) p); + dtype = CVT_get_numeric(q, l, &scale, p); node->litDesc.dsc_dtype = dtype; switch (dtype) @@ -6601,6 +6807,9 @@ DmlNode* LiteralNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* case dtype_double: node->litDesc.dsc_length = sizeof(double); break; + case dtype_dec128: + node->litDesc.dsc_length = sizeof(Decimal128); + break; case dtype_long: node->litDesc.dsc_length = sizeof(SLONG); node->litDesc.dsc_scale = (SCHAR) scale; @@ -6668,13 +6877,15 @@ void LiteralNode::genConstant(DsqlCompilerScratch* dsqlScratch, const dsc* desc, break; case dtype_double: + case dtype_dec128: { // this is used for approximate/large numeric literal // which is transmitted to the engine as a string. GEN_descriptor(dsqlScratch, desc, true); - // Length of string literal, cast because it could be > 127 bytes. - const USHORT l = (USHORT)(UCHAR) desc->dsc_scale; + + // Length of string literal - keep it in sub_type which is unused + const USHORT l = desc->dsc_sub_type; if (negateValue) { @@ -6933,7 +7144,7 @@ bool LiteralNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const LiteralNode* o = other->as(); + const LiteralNode* o = nodeAs(other); fb_assert(o); if (!DSC_EQUIV(&litDesc, &o->litDesc, true)) @@ -6949,10 +7160,11 @@ bool LiteralNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const LiteralNode* const otherNode = other->as(); + const LiteralNode* const otherNode = nodeAs(other); fb_assert(otherNode); + thread_db* tdbb = JRD_get_thread_data(); - return !MOV_compare(&litDesc, &otherNode->litDesc); + return !MOV_compare(tdbb, &litDesc, &otherNode->litDesc); } ValueExprNode* LiteralNode::pass2(thread_db* tdbb, CompilerScratch* csb) @@ -6971,6 +7183,43 @@ dsc* LiteralNode::execute(thread_db* /*tdbb*/, jrd_req* /*request*/) const return const_cast(&litDesc); } +void LiteralNode::fixMinSInt64() +{ + // MIN_SINT64 should be stored as BIGINT, not DECFLOAT + + const UCHAR* s = litDesc.dsc_address; + const char* minSInt64 = "9223372036854775808"; + bool hasDot = false; + int scale = 0; + + for (const UCHAR* s = litDesc.dsc_address; *s; ++s) + { + if (*s == '.') + { + if (hasDot) + return; + hasDot = true; + } + else if (*s == *minSInt64++) + { + if (hasDot) + scale--; + } + else + return; + } + + if (*minSInt64) + return; + + SINT64* valuePtr = FB_NEW_POOL(getPool()) SINT64(QUADCONST(0x8000000000000000)); + litDesc.dsc_dtype = dtype_int64; + litDesc.dsc_length = sizeof(SINT64); + litDesc.dsc_scale = scale; + litDesc.dsc_sub_type = 0; + litDesc.dsc_address = reinterpret_cast(valuePtr); +} + //-------------------- @@ -7101,7 +7350,7 @@ void DsqlMapNode::setParameterName(dsql_par* parameter) const const ValueExprNode* nestNode = map->map_node; const DsqlMapNode* mapNode; - while ((mapNode = ExprNode::as(nestNode))) + while ((mapNode = nodeAs(nestNode))) { // Skip all the DsqlMapNodes. nestNode = mapNode->map->map_node; @@ -7117,25 +7366,25 @@ void DsqlMapNode::setParameterName(dsql_par* parameter) const const DerivedFieldNode* derivedField; const RecordKeyNode* dbKeyNode; - if ((aggNode = ExprNode::as(nestNode))) + if ((aggNode = nodeAs(nestNode))) aggNode->setParameterName(parameter); - else if ((aliasNode = ExprNode::as(nestNode))) + else if ((aliasNode = nodeAs(nestNode))) { parameter->par_alias = aliasNode->name; alias = aliasNode->value; - fieldNode = ExprNode::as(alias); + fieldNode = nodeAs(alias); } - else if ((literalNode = ExprNode::as(nestNode))) + else if ((literalNode = nodeAs(nestNode))) literalNode->setParameterName(parameter); - else if ((dbKeyNode = ExprNode::as(nestNode))) + else if ((dbKeyNode = nodeAs(nestNode))) nameAlias = dbKeyNode->getAlias(false); - else if ((derivedField = ExprNode::as(nestNode))) + else if ((derivedField = nodeAs(nestNode))) { parameter->par_alias = derivedField->name; alias = derivedField->value; - fieldNode = ExprNode::as(alias); + fieldNode = nodeAs(alias); } - else if ((fieldNode = ExprNode::as(nestNode))) + else if ((fieldNode = nodeAs(nestNode))) nameAlias = fieldNode->dsqlField->fld_name.c_str(); const dsql_ctx* context = NULL; @@ -7177,7 +7426,7 @@ void DsqlMapNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) bool DsqlMapNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const { - const DsqlMapNode* o = other->as(); + const DsqlMapNode* o = nodeAs(other); return o && PASS1_node_match(map->map_node, o->map->map_node, ignoreMapCast); } @@ -7296,12 +7545,12 @@ void DerivedFieldNode::setParameterName(dsql_par* parameter) const const FieldNode* fieldNode; const RecordKeyNode* dbKeyNode; - if ((fieldNode = value->as())) + if ((fieldNode = nodeAs(value))) { parameter->par_name = fieldNode->dsqlField->fld_name.c_str(); context = fieldNode->dsqlContext; } - else if ((dbKeyNode = value->as())) + else if ((dbKeyNode = nodeAs(value))) dbKeyNode->setParameterName(parameter); parameter->par_alias = name; @@ -7317,11 +7566,11 @@ void DerivedFieldNode::genBlr(DsqlCompilerScratch* dsqlScratch) ValueExprNode* val = value; - while (val->is()) - val = val->as()->value; + while (nodeIs(val)) + val = nodeAs(val)->value; - if (!val->is() && !val->is() && - !val->is() && !val->is()) + if (!nodeIs(val) && !nodeIs(val) && + !nodeIs(val) && !nodeIs(val)) { if (context->ctx_main_derived_contexts.hasData()) { @@ -7368,7 +7617,7 @@ void DerivedFieldNode::genBlr(DsqlCompilerScratch* dsqlScratch) else if (!(dsqlScratch->flags & DsqlCompilerScratch::FLAG_FETCH) && !(context->ctx_flags & CTX_system) && (context->ctx_flags & CTX_cursor) && - val->is()) + nodeIs(val)) { // ASF: FieldNode::execute does not verify rpb_number.isValid(), and due to system triggers // and also singular queries, we cannot start to do it. So to fix CORE-4488, we introduce @@ -7376,7 +7625,7 @@ void DerivedFieldNode::genBlr(DsqlCompilerScratch* dsqlScratch) // FieldNode::execute by a test of rpb_number.isValid(). dsqlScratch->appendUChar(blr_derived_expr); dsqlScratch->appendUChar(1); - GEN_stuff_context(dsqlScratch, val->as()->dsqlContext); + GEN_stuff_context(dsqlScratch, nodeAs(val)->dsqlContext); } GEN_expr(dsqlScratch, value); @@ -7398,6 +7647,10 @@ NegateNode::NegateNode(MemoryPool& pool, ValueExprNode* aArg) arg(aArg) { addChildNode(arg, arg); + + LiteralNode* literal = nodeAs(arg); + if (literal && literal->litDesc.dsc_dtype == dtype_dec128) + literal->fixMinSInt64(); } DmlNode* NegateNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, const UCHAR /*blrOp*/) @@ -7428,17 +7681,17 @@ void NegateNode::setParameterName(dsql_par* parameter) const const ValueExprNode* innerNode = arg; const NegateNode* innerNegateNode; - while ((innerNegateNode = ExprNode::as(innerNode))) + while ((innerNegateNode = nodeAs(innerNode))) { innerNode = innerNegateNode->arg; ++level; } - if (ExprNode::is(innerNode) || ExprNode::is(innerNode)) + if (nodeIs(innerNode) || nodeIs(innerNode)) parameter->par_name = parameter->par_alias = "CONSTANT"; else if (!level) { - const ArithmeticNode* arithmeticNode = ExprNode::as(innerNode); + const ArithmeticNode* arithmeticNode = nodeAs(innerNode); if (arithmeticNode && ( /*arithmeticNode->blrOp == blr_add || @@ -7459,7 +7712,7 @@ bool NegateNode::setParameterType(DsqlCompilerScratch* dsqlScratch, void NegateNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - LiteralNode* literal = arg->as(); + LiteralNode* literal = nodeAs(arg); if (literal && DTYPE_IS_NUMERIC(literal->litDesc.dsc_dtype)) LiteralNode::genConstant(dsqlScratch, &literal->litDesc, true); @@ -7474,7 +7727,7 @@ void NegateNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { MAKE_desc(dsqlScratch, desc, arg); - if (arg->is()) + if (nodeIs(arg)) { // -NULL = NULL of INT desc->makeLong(0); @@ -7511,7 +7764,7 @@ void NegateNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) void NegateNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { arg->getDesc(tdbb, csb, desc); - nodFlags = arg->nodFlags & FLAG_DOUBLE; + nodFlags = arg->nodFlags & (FLAG_DOUBLE | FLAG_DECFLOAT); if (desc->dsc_dtype == dtype_quad) IBERROR(224); // msg 224 quad word arithmetic not supported @@ -7568,6 +7821,14 @@ dsc* NegateNode::execute(thread_db* tdbb, jrd_req* request) const impure->vlu_misc.vlu_double = -impure->vlu_misc.vlu_double; break; + case dtype_dec64: + impure->vlu_misc.vlu_dec64 = impure->vlu_misc.vlu_dec64.neg(); + break; + + case dtype_dec128: + impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.neg(); + break; + case dtype_int64: if (impure->vlu_misc.vlu_int64 == MIN_SINT64) ERR_post(Arg::Gds(isc_exception_integer_overflow)); @@ -7575,7 +7836,7 @@ dsc* NegateNode::execute(thread_db* tdbb, jrd_req* request) const break; default: - impure->vlu_misc.vlu_double = -MOV_get_double(&impure->vlu_desc); + impure->vlu_misc.vlu_double = -MOV_get_double(tdbb, &impure->vlu_desc); impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE; impure->vlu_desc.dsc_length = sizeof(double); impure->vlu_desc.dsc_scale = 0; @@ -7702,7 +7963,7 @@ bool OrderNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const OrderNode* o = other->as(); + const OrderNode* o = nodeAs(other); return o && descending == o->descending && nullsPlacement == o->nullsPlacement; } @@ -7716,7 +7977,7 @@ bool WindowClause::Frame::sameAs(const ExprNode* other, bool ignoreStreams) cons if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const Frame* const otherNode = other->as(); + const Frame* const otherNode = nodeAs(other); fb_assert(otherNode); return bound == otherNode->bound; @@ -7748,7 +8009,7 @@ bool WindowClause::FrameExtent::sameAs(const ExprNode* other, bool ignoreStreams if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const FrameExtent* const otherNode = other->as(); + const FrameExtent* const otherNode = nodeAs(other); fb_assert(otherNode); return unit == otherNode->unit; @@ -7857,7 +8118,7 @@ WindowClause* WindowClause::dsqlPass(DsqlCompilerScratch* dsqlScratch) } else { - OrderNode* key = node->order->items[0]->as(); + OrderNode* key = nodeAs(node->order->items[0]); fb_assert(key); dsc desc; @@ -8086,7 +8347,7 @@ ValueExprNode* OverNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) OverNode* node = FB_NEW_POOL(getPool()) OverNode(getPool(), static_cast(doDsqlPass(dsqlScratch, aggExpr)), doDsqlPass(dsqlScratch, refWindow)); - const AggNode* aggNode = node->aggExpr->as(); + const AggNode* aggNode = nodeAs(node->aggExpr); if (node->window && node->window->extent && @@ -8346,7 +8607,7 @@ void ParameterNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) bool ParameterNode::dsqlMatch(const ExprNode* other, bool /*ignoreMapCast*/) const { - const ParameterNode* o = other->as(); + const ParameterNode* o = nodeAs(other); return o && dsqlParameter->par_index == o->dsqlParameter->par_index; } @@ -8412,7 +8673,7 @@ dsc* ParameterNode::execute(thread_db* tdbb, jrd_req* request) const if (argFlag) { desc = EVL_expr(tdbb, request, argFlag); - if (MOV_get_long(desc, 0)) + if (MOV_get_long(tdbb, desc, 0)) request->req_flags |= req_null; } @@ -8788,7 +9049,7 @@ bool RecordKeyNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const RecordKeyNode* o = other->as(); + const RecordKeyNode* o = nodeAs(other); fb_assert(o); return blrOp == o->blrOp; @@ -8799,7 +9060,7 @@ bool RecordKeyNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const RecordKeyNode* const otherNode = other->as(); + const RecordKeyNode* const otherNode = nodeAs(other); fb_assert(otherNode); return blrOp == otherNode->blrOp && (ignoreStreams || recStream == otherNode->recStream); @@ -8836,7 +9097,7 @@ ValueExprNode* RecordKeyNode::pass1(thread_db* tdbb, CompilerScratch* csb) for (ValueExprNodeStack::iterator i(stack); i.hasData(); ++i) { #ifdef CMP_DEBUG - csb->dump(" %d", ExprNode::as(i.object())->recStream); + csb->dump(" %d", nodeAs(i.object())->recStream); #endif ValueIfNode* valueIfNode = FB_NEW_POOL(csb->csb_pool) ValueIfNode(csb->csb_pool); @@ -9090,7 +9351,7 @@ DmlNode* ScalarNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* c void ScalarNode::getDesc(thread_db* /*tdbb*/, CompilerScratch* csb, dsc* desc) { - FieldNode* fieldNode = field->as(); + FieldNode* fieldNode = nodeAs(field); fb_assert(fieldNode); jrd_rel* relation = csb->csb_rpt[fieldNode->fieldStream].csb_relation; @@ -9151,7 +9412,7 @@ dsc* ScalarNode::execute(thread_db* tdbb, jrd_req* request) const const dsc* temp = EVL_expr(tdbb, request, subscript); if (temp && !(request->req_flags & req_null)) - numSubscripts[iter++] = MOV_get_long(temp, 0); + numSubscripts[iter++] = MOV_get_long(tdbb, temp, 0); else return NULL; } @@ -9176,18 +9437,18 @@ DmlNode* StmtExprNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* node->expr = PAR_parse_value(tdbb, csb); // Avoid blr_stmt_expr in a BLR expression header - CompoundStmtNode* const stmt = node->stmt->as(); + CompoundStmtNode* const stmt = nodeAs(node->stmt); if (stmt) { if (stmt->statements.getCount() != 2 || - !stmt->statements[0]->is() || - !stmt->statements[1]->is()) + !nodeIs(stmt->statements[0]) || + !nodeIs(stmt->statements[1])) { return node->expr; } } - else if (!node->stmt->is()) + else if (!nodeIs(node->stmt)) return node->expr; return node; @@ -9316,7 +9577,7 @@ bool StrCaseNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const StrCaseNode* o = other->as(); + const StrCaseNode* o = nodeAs(other); fb_assert(o); return blrOp == o->blrOp; @@ -9327,7 +9588,7 @@ bool StrCaseNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const StrCaseNode* const otherNode = other->as(); + const StrCaseNode* const otherNode = nodeAs(other); fb_assert(otherNode); return blrOp == otherNode->blrOp; @@ -9399,7 +9660,7 @@ dsc* StrCaseNode::execute(thread_db* tdbb, jrd_req* request) const USHORT ttype; dsc desc; - desc.dsc_length = MOV_get_string_ptr(value, &ttype, &ptr, &temp, sizeof(temp)); + desc.dsc_length = MOV_get_string_ptr(tdbb, value, &ttype, &ptr, &temp, sizeof(temp)); desc.dsc_dtype = dtype_text; desc.dsc_address = NULL; desc.setTextType(ttype); @@ -9531,7 +9792,7 @@ bool StrLenNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const StrLenNode* o = other->as(); + const StrLenNode* o = nodeAs(other); fb_assert(o); return blrSubOp == o->blrSubOp; @@ -9542,7 +9803,7 @@ bool StrLenNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const StrLenNode* const otherNode = other->as(); + const StrLenNode* const otherNode = nodeAs(other); fb_assert(otherNode); return blrSubOp == otherNode->blrSubOp; @@ -9629,7 +9890,7 @@ dsc* StrLenNode::execute(thread_db* tdbb, jrd_req* request) const USHORT ttype; UCHAR* p; - length = MOV_get_string_ptr(value, &ttype, &p, &temp, sizeof(temp)); + length = MOV_get_string_ptr(tdbb, value, &ttype, &p, &temp, sizeof(temp)); switch (blrSubOp) { @@ -9734,7 +9995,7 @@ ValueExprNode* SubQueryNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) const DsqlContextStack::iterator base(*dsqlScratch->context); - RseNode* rse = PASS1_rse(dsqlScratch, dsqlRse->as(), false); + RseNode* rse = PASS1_rse(dsqlScratch, nodeAs(dsqlRse), false); SubQueryNode* node = FB_NEW_POOL(getPool()) SubQueryNode(getPool(), blrOp, rse, rse->dsqlSelectList->items[0], FB_NEW_POOL(getPool()) NullNode(getPool())); @@ -9791,7 +10052,7 @@ bool SubQueryNode::dsqlFieldFinder(FieldFinder& visitor) ValueExprNode* SubQueryNode::dsqlFieldRemapper(FieldRemapper& visitor) { doDsqlFieldRemapper(visitor, dsqlRse); - value1 = dsqlRse->as()->dsqlSelectList->items[0]; + value1 = nodeAs(dsqlRse)->dsqlSelectList->items[0]; return this; } @@ -9835,6 +10096,17 @@ void SubQueryNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) if (blrOp == blr_average) { + if (DTYPE_IS_DECFLOAT(desc->dsc_dtype)) + { + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + nodFlags |= FLAG_DECFLOAT; + return; + } + if (!(DTYPE_IS_NUMERIC(desc->dsc_dtype) || DTYPE_IS_TEXT(desc->dsc_dtype))) { if (desc->dsc_dtype != dtype_unknown) @@ -9884,6 +10156,16 @@ void SubQueryNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) nodFlags |= FLAG_DOUBLE; return; + case dtype_dec64: + case dtype_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + desc->dsc_scale = 0; + desc->dsc_sub_type = 0; + desc->dsc_flags = 0; + nodFlags |= FLAG_DECFLOAT; + return; + case dtype_sql_time: case dtype_sql_date: case dtype_timestamp: @@ -9954,18 +10236,17 @@ ValueExprNode* SubQueryNode::pass2(thread_db* tdbb, CompilerScratch* csb) impureOffset = CMP_impure(csb, sizeof(impure_value_ex)); - if (blrOp == blr_average) - nodFlags |= FLAG_DOUBLE; - else if (blrOp == blr_total) { dsc desc; getDesc(tdbb, csb, &desc); } + if (blrOp == blr_average && !(nodFlags & FLAG_DECFLOAT)) + nodFlags |= FLAG_DOUBLE; // Bind values of invariant nodes to top-level RSE (if present). if ((nodFlags & FLAG_INVARIANT) && csb->csb_current_nodes.hasData()) { - RseNode* topRseNode = csb->csb_current_nodes[0]->as(); + RseNode* topRseNode = nodeAs(csb->csb_current_nodes[0]); fb_assert(topRseNode); if (!topRseNode->rse_invariants) @@ -10049,7 +10330,7 @@ dsc* SubQueryNode::execute(thread_db* tdbb, jrd_req* request) const int result; - if (flag || ((result = MOV_compare(value, desc)) < 0 && blrOp == blr_minimum) || + if (flag || ((result = MOV_compare(tdbb, value, desc)) < 0 && blrOp == blr_minimum) || (blrOp != blr_minimum && result > 0)) { flag = 0; @@ -10086,7 +10367,7 @@ dsc* SubQueryNode::execute(thread_db* tdbb, jrd_req* request) const if (!count) break; - d = MOV_get_double(&impure->vlu_desc); + d = MOV_get_double(tdbb, &impure->vlu_desc); impure->vlu_misc.vlu_double = d / count; impure->vlu_desc.dsc_dtype = DEFAULT_DOUBLE; impure->vlu_desc.dsc_length = sizeof(double); @@ -10109,7 +10390,7 @@ dsc* SubQueryNode::execute(thread_db* tdbb, jrd_req* request) const break; default: - BUGCHECK(233); // msg 233 eval_statistical: invalid operation + SOFT_BUGCHECK(233); // msg 233 eval_statistical: invalid operation } } catch (const Exception&) @@ -10243,7 +10524,7 @@ void SubstringNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) { MAKE_desc(dsqlScratch, &desc3, length); - if (!length->is()) + if (!nodeIs(length)) desc3.dsc_address = NULL; } @@ -10258,7 +10539,7 @@ void SubstringNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) ValueExprNode* offsetNode = start; ValueExprNode* decrementNode = NULL; - ArithmeticNode* arithmeticNode = offsetNode->as(); + ArithmeticNode* arithmeticNode = nodeAs(offsetNode); // ASF: This code is very strange. The DSQL node is created as dialect 1, but only the dialect // 3 is verified here. Also, this task seems unnecessary here, as it must be done during @@ -10282,9 +10563,9 @@ void SubstringNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) desc->dsc_flags |= DSC_null; else { - if (length->is() && desc2.dsc_dtype == dtype_long) + if (nodeIs(length) && desc2.dsc_dtype == dtype_long) { - const SLONG len = MOV_get_long(&desc2, 0); + const SLONG len = MOV_get_long(tdbb, &desc2, 0); if (len < 0) ERR_post(Arg::Gds(isc_bad_substring_length) << Arg::Num(len)); @@ -10338,8 +10619,8 @@ dsc* SubstringNode::execute(thread_db* tdbb, jrd_req* request) const dsc* SubstringNode::perform(thread_db* tdbb, impure_value* impure, const dsc* valueDsc, const dsc* startDsc, const dsc* lengthDsc) { - SINT64 sStart = MOV_get_long(startDsc, 0); - SINT64 sLength = MOV_get_long(lengthDsc, 0); + SINT64 sStart = MOV_get_long(tdbb, startDsc, 0); + SINT64 sLength = MOV_get_long(tdbb, lengthDsc, 0); if (sLength < 0) status_exception::raise(Arg::Gds(isc_bad_substring_length) << Arg::Num(sLength)); @@ -10434,7 +10715,7 @@ dsc* SubstringNode::perform(thread_db* tdbb, impure_value* impure, const dsc* va // they aren't accepted, so they will cause error() to be called anyway. VaryStr<32> temp; USHORT ttype; - desc.dsc_length = MOV_get_string_ptr(valueDsc, &ttype, &desc.dsc_address, + desc.dsc_length = MOV_get_string_ptr(tdbb, valueDsc, &ttype, &desc.dsc_address, &temp, sizeof(temp)); desc.setTextType(ttype); @@ -10588,11 +10869,11 @@ ValueExprNode* SubstringSimilarNode::pass1(thread_db* tdbb, CompilerScratch* csb // If there is no top-level RSE present and patterns are not constant, unmark node as invariant // because it may be dependent on data or variables. - if ((nodFlags & FLAG_INVARIANT) && (!pattern->is() || !escape->is())) + if ((nodFlags & FLAG_INVARIANT) && (!nodeIs(pattern) || !nodeIs(escape))) { for (const auto& ctxNode : csb->csb_current_nodes) { - if (ctxNode->as()) + if (nodeAs(ctxNode)) return this; } @@ -10821,7 +11102,7 @@ void SysFuncCallNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) // dsc_address is verified in makeFunc to get literals. If the node is not a // literal, set it to NULL, to prevent wrong interpretation of offsets as // pointers - CORE-2612. - if (!arg->is()) + if (!nodeIs(arg)) targetDesc->dsc_address = NULL; } @@ -10846,7 +11127,7 @@ bool SysFuncCallNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const SysFuncCallNode* otherNode = other->as(); + const SysFuncCallNode* otherNode = nodeAs(other); return name == otherNode->name; } @@ -10856,7 +11137,7 @@ bool SysFuncCallNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const SysFuncCallNode* const otherNode = other->as(); + const SysFuncCallNode* const otherNode = nodeAs(other); fb_assert(otherNode); return function && function == otherNode->function; @@ -11077,7 +11358,7 @@ bool TrimNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const TrimNode* o = other->as(); + const TrimNode* o = nodeAs(other); fb_assert(o); return where == o->where; @@ -11088,7 +11369,7 @@ bool TrimNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const TrimNode* const otherNode = other->as(); + const TrimNode* const otherNode = nodeAs(other); fb_assert(otherNode); return where == otherNode->where; @@ -11443,7 +11724,7 @@ bool UdfCallNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const if (!ExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const UdfCallNode* otherNode = other->as(); + const UdfCallNode* otherNode = nodeAs(other); return name == otherNode->name; } @@ -11453,7 +11734,7 @@ bool UdfCallNode::sameAs(const ExprNode* other, bool ignoreStreams) const if (!ExprNode::sameAs(other, ignoreStreams)) return false; - const UdfCallNode* const otherNode = other->as(); + const UdfCallNode* const otherNode = nodeAs(other); fb_assert(otherNode); return function && function == otherNode->function; @@ -11805,31 +12086,31 @@ DmlNode* ValueIfNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* // Get rid of blr_stmt_expr expressions. // Coalesce. - MissingBoolNode* missing = node->condition->as(); + MissingBoolNode* missing = nodeAs(node->condition); if (missing) { - StmtExprNode* missingCond = missing->arg->as(); + StmtExprNode* missingCond = nodeAs(missing->arg); if (!missingCond) return node; - CompoundStmtNode* stmt = missingCond->stmt->as(); + CompoundStmtNode* stmt = nodeAs(missingCond->stmt); DeclareVariableNode* declStmt = NULL; AssignmentNode* assignStmt; if (stmt) { if (stmt->statements.getCount() != 2 || - !(declStmt = stmt->statements[0]->as()) || - !(assignStmt = stmt->statements[1]->as())) + !(declStmt = nodeAs(stmt->statements[0])) || + !(assignStmt = nodeAs(stmt->statements[1]))) { return node; } } - else if (!(assignStmt = missingCond->stmt->as())) + else if (!(assignStmt = nodeAs(missingCond->stmt))) return node; - VariableNode* var = node->falseValue->as(); - VariableNode* var2 = assignStmt->asgnTo->as(); + VariableNode* var = nodeAs(node->falseValue); + VariableNode* var2 = nodeAs(assignStmt->asgnTo); if (!var || !var2 || var->varId != var2->varId || (declStmt && declStmt->varId != var->varId)) return node; @@ -11843,30 +12124,30 @@ DmlNode* ValueIfNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* } // Decode. - ComparativeBoolNode* cmp = node->condition->as(); + ComparativeBoolNode* cmp = nodeAs(node->condition); if (cmp && cmp->blrOp == blr_eql) { - StmtExprNode* cmpCond = cmp->arg1->as(); + StmtExprNode* cmpCond = nodeAs(cmp->arg1); if (!cmpCond) return node; - CompoundStmtNode* stmt = cmpCond->stmt->as(); + CompoundStmtNode* stmt = nodeAs(cmpCond->stmt); DeclareVariableNode* declStmt = NULL; AssignmentNode* assignStmt; if (stmt) { if (stmt->statements.getCount() != 2 || - !(declStmt = stmt->statements[0]->as()) || - !(assignStmt = stmt->statements[1]->as())) + !(declStmt = nodeAs(stmt->statements[0])) || + !(assignStmt = nodeAs(stmt->statements[1]))) { return node; } } - else if (!(assignStmt = cmpCond->stmt->as())) + else if (!(assignStmt = nodeAs(cmpCond->stmt))) return node; - VariableNode* var = assignStmt->asgnTo->as(); + VariableNode* var = nodeAs(assignStmt->asgnTo); if (!var || (declStmt && declStmt->varId != var->varId)) return node; @@ -11880,13 +12161,13 @@ DmlNode* ValueIfNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* decodeNode->values->add(node->trueValue); ValueExprNode* last = node->falseValue; - while ((node = last->as())) + while ((node = nodeAs(last))) { - ComparativeBoolNode* cmp = node->condition->as(); + ComparativeBoolNode* cmp = nodeAs(node->condition); if (!cmp || cmp->blrOp != blr_eql) break; - VariableNode* var2 = cmp->arg1->as(); + VariableNode* var2 = nodeAs(cmp->arg1); if (!var2 || var2->varId != var->varId) break; @@ -11969,7 +12250,7 @@ void ValueIfNode::make(DsqlCompilerScratch* dsqlScratch, dsc* desc) void ValueIfNode::getDesc(thread_db* tdbb, CompilerScratch* csb, dsc* desc) { - ValueExprNode* val = trueValue->is() ? falseValue : trueValue; + ValueExprNode* val = nodeIs(trueValue) ? falseValue : trueValue; val->getDesc(tdbb, csb, desc); } @@ -12088,7 +12369,7 @@ void VariableNode::make(DsqlCompilerScratch* /*dsqlScratch*/, dsc* desc) bool VariableNode::dsqlMatch(const ExprNode* other, bool /*ignoreMapCast*/) const { - const VariableNode* o = other->as(); + const VariableNode* o = nodeAs(other); if (!o) return false; @@ -12215,6 +12496,7 @@ static SINT64 getDayFraction(const dsc* d) { dsc result; double result_days; + thread_db* tdbb = JRD_get_thread_data(); result.dsc_dtype = dtype_double; result.dsc_scale = 0; @@ -12224,7 +12506,7 @@ static SINT64 getDayFraction(const dsc* d) result.dsc_address = reinterpret_cast(&result_days); // Convert the input number to a double - CVT_move(d, &result); + CVT_move(d, &result, tdbb->getAttachment()->att_dec_status); // There's likely some loss of precision here due to rounding of number @@ -12256,6 +12538,7 @@ static SINT64 getDayFraction(const dsc* d) // This is derived from the ISC_TIME_SECONDS_PRECISION. static SINT64 getTimeStampToIscTicks(const dsc* d) { + thread_db* tdbb = JRD_get_thread_data(); dsc result; GDS_TIMESTAMP result_timestamp; @@ -12266,7 +12549,7 @@ static SINT64 getTimeStampToIscTicks(const dsc* d) result.dsc_length = sizeof(GDS_TIMESTAMP); result.dsc_address = reinterpret_cast(&result_timestamp); - CVT_move(d, &result); + CVT_move(d, &result, tdbb->getAttachment()->att_dec_status); return ((SINT64) result_timestamp.timestamp_date) * ISC_TICKS_PER_DAY + (SINT64) result_timestamp.timestamp_time; diff --git a/src/dsql/ExprNodes.h b/src/dsql/ExprNodes.h index 3eea3b08f3..b490a9c0b5 100644 --- a/src/dsql/ExprNodes.h +++ b/src/dsql/ExprNodes.h @@ -797,6 +797,8 @@ public: return *reinterpret_cast(litDesc.dsc_address); } + void fixMinSInt64(); + public: const IntlString* dsqlStr; dsc litDesc; @@ -1059,7 +1061,7 @@ public: if (!ListExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const Frame* o = other->as(); + const Frame* o = nodeAs(other); fb_assert(o); return bound == o->bound; @@ -1128,7 +1130,7 @@ public: if (!ListExprNode::dsqlMatch(other, ignoreMapCast)) return false; - const FrameExtent* o = other->as(); + const FrameExtent* o = nodeAs(other); fb_assert(o); return unit == o->unit; @@ -1197,7 +1199,7 @@ public: if (!DsqlNode::dsqlMatch(other, ignoreMapCast)) return false; - const WindowClause* o = other->as(); + const WindowClause* o = nodeAs(other); fb_assert(o); return exclusion == o->exclusion; diff --git a/src/dsql/Nodes.h b/src/dsql/Nodes.h index 25380047d7..97cebccb46 100644 --- a/src/dsql/Nodes.h +++ b/src/dsql/Nodes.h @@ -281,21 +281,25 @@ public: }; -class SetSessionNode : public Node +class SessionManagementNode : public Node { public: - enum Type { TYPE_IDLE_TIMEOUT, TYPE_STMT_TIMEOUT }; - - SetSessionNode(MemoryPool& pool, Type aType, ULONG aVal, UCHAR blr_timepart); + explicit SessionManagementNode(MemoryPool& pool) + : Node(pool) + { + } public: - virtual Firebird::string internalPrint(NodePrinter& printer) const; - virtual SetSessionNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request) const; + virtual SessionManagementNode* dsqlPass(DsqlCompilerScratch* dsqlScratch) + { + Node::dsqlPass(dsqlScratch); -private: - Type m_type; - ULONG m_value; + dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT); + + return this; + } + + virtual void execute(thread_db* tdbb, dsql_req* request) const = 0; }; @@ -352,6 +356,37 @@ public: }; +template static To* nodeAs(From* fromNode) +{ + return fromNode && fromNode->type == To::TYPE ? static_cast(fromNode) : NULL; +} + +template static To* nodeAs(NestConst& fromNode) +{ + return fromNode && fromNode->type == To::TYPE ? static_cast(fromNode.getObject()) : NULL; +} + +template static const To* nodeAs(const From* fromNode) +{ + return fromNode && fromNode->type == To::TYPE ? static_cast(fromNode) : NULL; +} + +template static const To* nodeAs(const NestConst& fromNode) +{ + return fromNode && fromNode->type == To::TYPE ? static_cast(fromNode.getObject()) : NULL; +} + +template static bool nodeIs(const From* fromNode) +{ + return fromNode && fromNode->type == To::TYPE; +} + +template static bool nodeIs(const NestConst& fromNode) +{ + return fromNode && fromNode->type == To::TYPE; +} + + // Stores a reference to a specialized ExprNode. // This class and NodeRefImpl exists for nodes to replace themselves (eg. pass1) in a type-safe way. class NodeRef @@ -489,7 +524,8 @@ public: // Value flags. static const unsigned FLAG_DOUBLE = 0x10; static const unsigned FLAG_DATE = 0x20; - static const unsigned FLAG_VALUE = 0x40; // Full value area required in impure space. + static const unsigned FLAG_DECFLOAT = 0x40; + static const unsigned FLAG_VALUE = 0x80; // Full value area required in impure space. explicit ExprNode(Type aType, MemoryPool& pool, Kind aKind) : DmlNode(pool, aKind), @@ -502,39 +538,6 @@ public: { } - template T* as() - { - const ExprNode* const thisPointer = this; // avoid warning - return thisPointer && type == T::TYPE ? static_cast(this) : NULL; - } - - template const T* as() const - { - const ExprNode* const thisPointer = this; // avoid warning - return thisPointer && type == T::TYPE ? static_cast(this) : NULL; - } - - template bool is() const - { - const ExprNode* const thisPointer = this; // avoid warning - return thisPointer && type == T::TYPE; - } - - template static T* as(LegacyType* node) - { - return node ? node->template as() : NULL; - } - - template static const T* as(const LegacyType* node) - { - return node ? node->template as() : NULL; - } - - template static bool is(const LegacyType* node) - { - return node ? node->template is() : false; - } - // Allocate and assign impure space for various nodes. template static void doPass2(thread_db* tdbb, CompilerScratch* csb, T** node) { diff --git a/src/dsql/Parser.cpp b/src/dsql/Parser.cpp index c650aa5a02..5b005417a9 100644 --- a/src/dsql/Parser.cpp +++ b/src/dsql/Parser.cpp @@ -22,6 +22,7 @@ #include "firebird.h" #include +#include #include "../dsql/Parser.h" #include "../dsql/chars.h" #include "../jrd/jrd.h" @@ -29,6 +30,12 @@ #include "../yvalve/keywords.h" #include "../jrd/intl_proto.h" +#ifdef HAVE_FLOAT_H +#include +#else +#define DBL_MAX_10_EXP 308 +#endif + using namespace Firebird; using namespace Jrd; @@ -925,15 +932,19 @@ int Parser::yylexAux() { // The following variables are used to recognize kinds of numbers. - bool have_error = false; // syntax error or value too large - bool have_digit = false; // we've seen a digit - bool have_decimal = false; // we've seen a '.' - bool have_exp = false; // digit ... [eE] - bool have_exp_sign = false; // digit ... [eE] {+-] - bool have_exp_digit = false; // digit ... [eE] ... digit + bool have_error = false; // syntax error or value too large + bool have_digit = false; // we've seen a digit + bool have_decimal = false; // we've seen a '.' + bool have_exp = false; // digit ... [eE] + bool have_exp_sign = false; // digit ... [eE] {+-] + bool have_exp_digit = false; // digit ... [eE] ... digit + bool have_overflow = false; // value of digits > MAX_SINT64 + bool positive_overflow = false; // number is exactly (MAX_SINT64 + 1) FB_UINT64 number = 0; + int expVal = 0; FB_UINT64 limit_by_10 = MAX_SINT64 / 10; - SCHAR scale = 0; + int scale = 0; + int expSign = 1; for (--lex.ptr; lex.ptr < lex.end; lex.ptr++) { @@ -953,10 +964,22 @@ int Parser::yylexAux() { // We've seen e or E, but nothing beyond that. if ( ('-' == c) || ('+' == c) ) + { have_exp_sign = true; + if ('-' == c) + expSign = -1; + } else if ( classes(c) & CHR_DIGIT ) + { // We have a digit: we haven't seen a sign yet, but it's too late now. have_exp_digit = have_exp_sign = true; + if (!have_overflow) + { + expVal = expVal * 10 + (c - '0'); + if (expVal > DBL_MAX_10_EXP) + have_overflow = true; + } + } else { // end of the token @@ -978,19 +1001,26 @@ int Parser::yylexAux() { // Before computing the next value, make sure there will be no overflow. - have_digit = true; - - if (number >= limit_by_10) + if (!have_overflow) { - // possibility of an overflow - if ((number > limit_by_10) || (c > '8')) + have_digit = true; + + if (number >= limit_by_10) { - have_error = true; - break; + // possibility of an overflow + if ((number > limit_by_10) || (c >= '8')) + { + have_overflow = true; + if ((number == limit_by_10) && (c == '8')) + positive_overflow = true; + } } } + else + positive_overflow = false; - number = number * 10 + (c - '0'); + if (!have_overflow) + number = number * 10 + (c - '0'); if (have_decimal) --scale; @@ -1009,15 +1039,48 @@ int Parser::yylexAux() { fb_assert(have_digit); - if (have_exp_digit) + if (positive_overflow) + have_overflow = false; + + if (scale < MIN_SCHAR || scale > MAX_SCHAR) { + have_overflow = true; + positive_overflow = false; + } + + // check for a more complex overflow case + if ((!have_overflow) && (expSign > 0) && (expVal > -scale)) + { + expVal += scale; + double maxNum = DBL_MAX / pow(10.0, expVal); + if (double(number) > maxNum) + { + have_overflow = true; + positive_overflow = false; + } + } + + // Should we use floating point type? + if (have_exp_digit || have_overflow || positive_overflow) + { + if (positive_overflow && scale) + { + yylval.lim64ptr = newLim64String( + Firebird::string(lex.last_token, lex.ptr - lex.last_token), scale); + lex.last_token_bk = lex.last_token; + lex.line_start_bk = lex.line_start; + lex.lines_bk = lex.lines; + + return TOK_LIMIT64_NUMBER; + } + yylval.stringPtr = newString( Firebird::string(lex.last_token, lex.ptr - lex.last_token)); lex.last_token_bk = lex.last_token; lex.line_start_bk = lex.line_start; lex.lines_bk = lex.lines; - return TOK_FLOAT_NUMBER; + return positive_overflow ? TOK_LIMIT64_INT : have_overflow ? TOK_DECIMAL_NUMBER : TOK_FLOAT_NUMBER; } if (!have_exp) diff --git a/src/dsql/Parser.h b/src/dsql/Parser.h index 7d195914e9..54fa694ec2 100644 --- a/src/dsql/Parser.h +++ b/src/dsql/Parser.h @@ -144,6 +144,11 @@ public: return FB_NEW_POOL(getPool()) Firebird::string(getPool(), s); } + Lim64String* newLim64String(const Firebird::string& s, int scale) + { + return FB_NEW_POOL(getPool()) Lim64String(getPool(), s, scale); + } + IntlString* newIntlString(const Firebird::string& s, const char* charSet = NULL) { return FB_NEW_POOL(getPool()) IntlString(getPool(), s, charSet); @@ -209,7 +214,7 @@ private: BoolExprNode* valueToBool(ValueExprNode* value) { - BoolAsValueNode* node = value->as(); + BoolAsValueNode* node = nodeAs(value); if (node) return node->boolean; diff --git a/src/dsql/StmtNodes.cpp b/src/dsql/StmtNodes.cpp index 41ce5ebb91..04e2a29979 100644 --- a/src/dsql/StmtNodes.cpp +++ b/src/dsql/StmtNodes.cpp @@ -195,7 +195,7 @@ namespace return NULL; // nod_returning was already processed - CompoundStmtNode* processedStmt = processed->as(); + CompoundStmtNode* processedStmt = nodeAs(processed); fb_assert(processed); // And we create a RETURNING node where the targets are already processed. @@ -211,7 +211,7 @@ namespace { AssignmentNode* temp = FB_NEW_POOL(scratch->getPool()) AssignmentNode(scratch->getPool()); temp->asgnFrom = *srcPtr; - temp->asgnTo = (*dstPtr)->as()->asgnTo; + temp->asgnTo = nodeAs(*dstPtr)->asgnTo; newNode->statements.add(temp); } @@ -291,7 +291,7 @@ void AssignmentNode::validateTarget(CompilerScratch* csb, const ValueExprNode* t { const FieldNode* fieldNode; - if ((fieldNode = target->as())) + if ((fieldNode = nodeAs(target))) { CompilerScratch::csb_repeat* tail = &csb->csb_rpt[fieldNode->fieldStream]; @@ -311,13 +311,13 @@ void AssignmentNode::validateTarget(CompilerScratch* csb, const ValueExprNode* t if (fieldNode->cursorNumber.specified) ERR_post(Arg::Gds(isc_read_only_field)); } - else if (!(target->is() || target->is() || target->is())) + else if (!(nodeIs(target) || nodeIs(target) || nodeIs(target))) ERR_post(Arg::Gds(isc_read_only_field)); } void AssignmentNode::dsqlValidateTarget(const ValueExprNode* target) { - const DerivedFieldNode* fieldNode = target->as(); + const DerivedFieldNode* fieldNode = nodeAs(target); if (fieldNode && fieldNode->context && (fieldNode->context->ctx_flags & (CTX_system | CTX_cursor)) == CTX_cursor) @@ -381,7 +381,7 @@ AssignmentNode* AssignmentNode::pass1(thread_db* tdbb, CompilerScratch* csb) StreamType stream; CompilerScratch::csb_repeat* tail; - if ((fieldNode = sub->as())) + if ((fieldNode = nodeAs(sub))) { stream = fieldNode->fieldStream; jrd_fld* field = MET_get_field(csb->csb_rpt[stream].csb_relation, fieldNode->fieldId); @@ -392,7 +392,7 @@ AssignmentNode* AssignmentNode::pass1(thread_db* tdbb, CompilerScratch* csb) sub = asgnTo; - if ((fieldNode = sub->as())) + if ((fieldNode = nodeAs(sub))) { stream = fieldNode->fieldStream; tail = &csb->csb_rpt[stream]; @@ -603,7 +603,7 @@ const StmtNode* BlockNode::execute(thread_db* tdbb, jrd_req* request, ExeState* ptr != end; ++ptr) { - const ErrorHandlerNode* const handlerNode = (*ptr)->as(); + const ErrorHandlerNode* const handlerNode = nodeAs(*ptr); if (testAndFixupError(tdbb, request, handlerNode->conditions)) { @@ -1189,7 +1189,7 @@ const StmtNode* CursorStmtNode::execute(thread_db* tdbb, jrd_req* request, ExeSt const dsc* desc = EVL_expr(tdbb, request, scrollExpr); const bool unknown = !desc || (request->req_flags & req_null); - const SINT64 offset = unknown ? 0 : MOV_get_int64(desc, 0); + const SINT64 offset = unknown ? 0 : MOV_get_int64(tdbb, desc, 0); switch (scrollOp) { @@ -2115,7 +2115,7 @@ StmtNode* EraseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) rse->dsqlFlags |= RecordSourceNode::DFLAG_SINGLETON; node->dsqlRse = rse; - node->dsqlRelation = rse->dsqlStreams->items[0]->as(); + node->dsqlRelation = nodeAs(rse->dsqlStreams->items[0]); node->statement = dsqlProcessReturning(dsqlScratch, dsqlReturning, statement); @@ -5153,9 +5153,9 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) forNode->dsqlForceSingular = true; // Get the already processed relations. - RseNode* processedRse = forNode->rse->dsqlStreams->items[0]->as(); + RseNode* processedRse = nodeAs(forNode->rse->dsqlStreams->items[0]); source = processedRse->dsqlStreams->items[0]; - target = processedRse->dsqlStreams->items[1]->as(); + target = nodeAs(processedRse->dsqlStreams->items[1]); DsqlContextStack usingCtxs; dsqlGetContexts(usingCtxs, source); @@ -5181,7 +5181,7 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // Separate the new and org values to process in correct contexts. for (FB_SIZE_T i = 0; i < stmts->statements.getCount(); ++i) { - AssignmentNode* const assign = stmts->statements[i]->as(); + AssignmentNode* const assign = nodeAs(stmts->statements[i]); fb_assert(assign); orgValues.add(assign->asgnFrom); newValues.add(assign->asgnTo); @@ -5345,7 +5345,7 @@ StmtNode* MergeNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) store->overrideClause = notMatched->overrideClause; bool needSavePoint; // unused - thisIf->trueAction = store = store->internalDsqlPass(dsqlScratch, false, needSavePoint)->as(); + thisIf->trueAction = store = nodeAs(store->internalDsqlPass(dsqlScratch, false, needSavePoint)); fb_assert(store); if (notMatched->condition) @@ -5521,11 +5521,8 @@ void MessageNode::setup(thread_db* tdbb, CompilerScratch* csb, USHORT message, U // So we only check even indexes, which is the actual parameter. if (itemInfo.isSpecial() && index % 2 == 0) { - csb->csb_dbg_info->argInfoToName.get( - ArgumentInfo(csb->csb_msg_number, index / 2), itemInfo.name); - - csb->csb_map_item_info.put(Item(Item::TYPE_PARAMETER, csb->csb_msg_number, index), - itemInfo); + csb->csb_dbg_info->argInfoToName.get(ArgumentInfo(csb->csb_msg_number, index / 2), itemInfo.name); + csb->csb_map_item_info.put(Item(Item::TYPE_PARAMETER, message, index), itemInfo); } } @@ -5654,18 +5651,18 @@ StmtNode* ModifyNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, bool up Array > orgValues, newValues; - CompoundStmtNode* assignments = statement->as(); + CompoundStmtNode* assignments = nodeAs(statement); fb_assert(assignments); for (FB_SIZE_T i = 0; i < assignments->statements.getCount(); ++i) { - AssignmentNode* const assign = assignments->statements[i]->as(); + AssignmentNode* const assign = nodeAs(assignments->statements[i]); fb_assert(assign); orgValues.add(assign->asgnFrom); newValues.add(assign->asgnTo); } - NestConst relation = dsqlRelation->as(); + NestConst relation = nodeAs(dsqlRelation); fb_assert(relation); NestConst* ptr; @@ -5854,7 +5851,7 @@ string ModifyNode::internalPrint(NodePrinter& printer) const void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - RseNode* rse = dsqlRse->as(); + RseNode* rse = nodeAs(dsqlRse); const dsql_msg* message = dsqlGenDmlHeader(dsqlScratch, rse); @@ -5884,7 +5881,7 @@ void ModifyNode::genBlr(DsqlCompilerScratch* dsqlScratch) ModifyNode* ModifyNode::pass1(thread_db* tdbb, CompilerScratch* csb) { - preprocessAssignments(tdbb, csb, newStream, statement->as(), NULL); + preprocessAssignments(tdbb, csb, newStream, nodeAs(statement), NULL); pass1Modify(tdbb, csb, this); @@ -6449,7 +6446,7 @@ DmlNode* StoreNode::parse(thread_db* tdbb, MemoryPool& pool, CompilerScratch* cs const UCHAR* blrPos = csb->csb_blr_reader.getPos(); - node->relationSource = PAR_parseRecordSource(tdbb, csb)->as(); + node->relationSource = nodeAs(PAR_parseRecordSource(tdbb, csb)); if (!node->relationSource) { @@ -6489,7 +6486,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, if (dsqlRse) { - SelectExprNode* selExpr = dsqlRse->as(); + SelectExprNode* selExpr = nodeAs(dsqlRse); fb_assert(selExpr); if (dsqlReturning || statement2) @@ -6540,13 +6537,13 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, const FieldNode* fieldNode; const DerivedFieldNode* derivedField; - if ((fieldNode = ExprNode::as(temp2))) + if ((fieldNode = nodeAs(temp2))) { tmp_ctx = fieldNode->dsqlContext; if (fieldNode->dsqlField) tmp_name = fieldNode->dsqlField->fld_name.c_str(); } - else if ((derivedField = ExprNode::as(temp2))) + else if ((derivedField = nodeAs(temp2))) { tmp_ctx = derivedField->context; tmp_name = derivedField->name.nullStr(); @@ -6594,7 +6591,7 @@ StmtNode* StoreNode::internalDsqlPass(DsqlCompilerScratch* dsqlScratch, if (!*ptr2) { - const FieldNode* field = (*ptr)->as(); + const FieldNode* field = nodeAs(*ptr); if (field && field->dsqlField) { @@ -6661,7 +6658,7 @@ StmtNode* StoreNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) StmtNode* node = SavepointEncloseNode::make(getPool(), dsqlScratch, internalDsqlPass(dsqlScratch, false, needSavePoint)); - if (!needSavePoint || node->is()) + if (!needSavePoint || nodeIs(node)) return node; return FB_NEW SavepointEncloseNode(getPool(), node); @@ -6687,7 +6684,7 @@ string StoreNode::internalPrint(NodePrinter& printer) const void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - const dsql_msg* message = dsqlGenDmlHeader(dsqlScratch, dsqlRse->as()); + const dsql_msg* message = dsqlGenDmlHeader(dsqlScratch, nodeAs(dsqlRse)); dsqlScratch->appendUChar(overrideClause.specified ? blr_store3 : (statement2 ? blr_store2 : blr_store)); @@ -6709,7 +6706,7 @@ void StoreNode::genBlr(DsqlCompilerScratch* dsqlScratch) StoreNode* StoreNode::pass1(thread_db* tdbb, CompilerScratch* csb) { - preprocessAssignments(tdbb, csb, relationSource->getStream(), statement->as(), &overrideClause); + preprocessAssignments(tdbb, csb, relationSource->getStream(), nodeAs(statement), &overrideClause); if (pass1Store(tdbb, csb, this)) makeDefaults(tdbb, csb); @@ -6871,7 +6868,7 @@ void StoreNode::makeDefaults(thread_db* tdbb, CompilerScratch* csb) if (assign) { - const FieldNode* fieldNode = assign->asgnTo->as(); + const FieldNode* fieldNode = nodeAs(assign->asgnTo); fb_assert(fieldNode); if (fieldNode && fieldNode->fieldStream == stream && fieldNode->fieldId == fieldId) @@ -6984,7 +6981,7 @@ const StmtNode* StoreNode::store(thread_db* tdbb, jrd_req* request, WhichTrigger switch (request->req_operation) { case jrd_req::req_evaluate: - if (!parentStmt->is()) + if (!nodeIs(parentStmt)) request->req_records_affected.clear(); request->req_records_affected.bumpModified(false); @@ -7208,7 +7205,7 @@ const StmtNode* UserSavepointNode::execute(thread_db* tdbb, jrd_req* request, Ex } default: - BUGCHECK(232); + SOFT_BUGCHECK(232); // msg 232 EVL_expr: invalid operation break; } @@ -7260,7 +7257,7 @@ SelectNode* SelectNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) // stored procedure occurs in the select list. In these cases all of stored procedure is // executed under savepoint for open cursor. - RseNode* rseNode = node->dsqlRse->as(); + RseNode* rseNode = nodeAs(node->dsqlRse); if (rseNode->dsqlOrder || rseNode->dsqlDistinct) { @@ -7288,7 +7285,7 @@ string SelectNode::internalPrint(NodePrinter& printer) const // Generate BLR for a SELECT statement. void SelectNode::genBlr(DsqlCompilerScratch* dsqlScratch) { - RseNode* const rse = dsqlRse->as(); + RseNode* const rse = nodeAs(dsqlRse); fb_assert(rse); DsqlCompiledStatement* const statement = dsqlScratch->getStatement(); @@ -7326,7 +7323,7 @@ void SelectNode::genBlr(DsqlCompilerScratch* dsqlScratch) RecordSourceNode* const item = *ptr2; RelationSourceNode* relNode; - if (item && (relNode = ExprNode::as(item))) + if (item && (relNode = nodeAs(item))) { context = relNode->dsqlContext; const dsql_rel* const relation = context->ctx_relation; @@ -7532,7 +7529,7 @@ const StmtNode* SetGeneratorNode::execute(thread_db* tdbb, jrd_req* request, Exe DDL_TRIGGER_ALTER_SEQUENCE, generator.name, NULL, *request->getStatement()->sqlText); dsc* const desc = EVL_expr(tdbb, request, value); - DPM_gen_id(tdbb, generator.id, true, MOV_get_int64(desc, 0)); + DPM_gen_id(tdbb, generator.id, true, MOV_get_int64(tdbb, desc, 0)); DdlNode::executeDdlTrigger(tdbb, transaction, DdlNode::DTW_AFTER, DDL_TRIGGER_ALTER_SEQUENCE, generator.name, NULL, *request->getStatement()->sqlText); @@ -7685,11 +7682,11 @@ const StmtNode* SuspendNode::execute(thread_db* tdbb, jrd_req* request, ExeState if (!(request->req_flags & req_proc_fetch)) return statement; - const CompoundStmtNode* list = parentStmt->as(); + const CompoundStmtNode* list = nodeAs(parentStmt); if (list && !list->parentStmt && list->statements[list->statements.getCount() - 1] == this) { - list = statement->as(); + list = nodeAs(statement); if (list && list->onlyAssignments && list->statements.hasData()) { @@ -7957,14 +7954,7 @@ void SetTransactionNode::genTableLock(DsqlCompilerScratch* dsqlScratch, //-------------------- -SetRoleNode* SetRoleNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) -{ - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SET_ROLE); - - return this; -} - -void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const +void SetRoleNode::execute(thread_db* tdbb, dsql_req* request) const { SET_TDBB(tdbb); Attachment* const attachment = tdbb->getAttachment(); @@ -7988,8 +7978,122 @@ void SetRoleNode::execute(thread_db* tdbb, dsql_req* request, jrd_tra** transact //-------------------- +namespace +{ + +struct TextCode +{ + const char* name; + USHORT val; +}; + +//#define FB_TEXTCODE(x) { STRINGIZE(x), x } +#define FB_TEXTCODE(x) { #x, x } + +const TextCode roundModes[] = { + FB_TEXTCODE(DEC_ROUND_CEILING), + FB_TEXTCODE(DEC_ROUND_UP), + FB_TEXTCODE(DEC_ROUND_HALF_UP), + FB_TEXTCODE(DEC_ROUND_HALF_EVEN), + FB_TEXTCODE(DEC_ROUND_HALF_DOWN), + FB_TEXTCODE(DEC_ROUND_DOWN), + FB_TEXTCODE(DEC_ROUND_FLOOR), + { "DEC_ROUND_REROUND", DEC_ROUND_05UP }, + { NULL, 0 } +}; + +//DEC_ROUND_ +//0123456789 +const unsigned FB_RMODE_OFFSET = 10; + +const TextCode ieeeTraps[] = { + FB_TEXTCODE(DEC_IEEE_754_Division_by_zero), + FB_TEXTCODE(DEC_IEEE_754_Inexact), + FB_TEXTCODE(DEC_IEEE_754_Invalid_operation), + FB_TEXTCODE(DEC_IEEE_754_Overflow), + FB_TEXTCODE(DEC_IEEE_754_Underflow), + { NULL, 0 } +}; + +//DEC_IEEE_754_ +//0123456789012 +const unsigned FB_TRAPS_OFFSET = 13; + +#undef FB_TEXTCODE + +const TextCode* getCodeByText(const MetaName& text, const TextCode* textCode, unsigned offset) +{ + NoCaseString name(text.c_str(), text.length()); + + for (const TextCode* tc = textCode; tc->name; ++tc) + { + if (name == &tc->name[offset]) + return tc; + } + + return nullptr; +} + +} + + +//-------------------- + + +SetRoundNode::SetRoundNode(MemoryPool& pool, Firebird::MetaName* name) + : SessionManagementNode(pool) +{ + fb_assert(name); + const TextCode* mode = getCodeByText(*name, roundModes, FB_RMODE_OFFSET); + if (!mode) + (Arg::Gds(isc_random) << "Invalid round mode for decfloat").raise(); + rndMode = mode->val; +} + +void SetRoundNode::execute(thread_db* tdbb, dsql_req* /*request*/) const +{ + SET_TDBB(tdbb); + Attachment* const attachment = tdbb->getAttachment(); + attachment->att_dec_status.roundingMode = rndMode; +} + + +//-------------------- + + +void SetTrapsNode::trap(Firebird::MetaName* name) +{ + fb_assert(name); + const TextCode* trap = getCodeByText(*name, ieeeTraps, FB_TRAPS_OFFSET); + if (!trap) + (Arg::Gds(isc_random) << "Invalid decfloat trap").raise(); + traps |= trap->val; +} + +void SetTrapsNode::execute(thread_db* tdbb, dsql_req* /*request*/) const +{ + SET_TDBB(tdbb); + Attachment* const attachment = tdbb->getAttachment(); + attachment->att_dec_status.decExtFlag = traps; +} + + +//-------------------- + + +void SetBindNode::execute(thread_db* tdbb, dsql_req* /*request*/) const +{ + SET_TDBB(tdbb); + Attachment* const attachment = tdbb->getAttachment(); + attachment->att_dec_binding = bind; +} + + +//-------------------- + + SetSessionNode::SetSessionNode(MemoryPool& pool, Type aType, ULONG aVal, UCHAR blr_timepart) - : Node(pool), + : SessionManagementNode(pool), m_type(aType), m_value(0) { @@ -8036,12 +8140,6 @@ string SetSessionNode::internalPrint(NodePrinter& printer) const return "SetSessionNode"; } -SetSessionNode* SetSessionNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) -{ - dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_SET_SESSION); - return this; -} - void SetSessionNode::execute(thread_db* tdbb, dsql_req* request) const { Attachment* att = tdbb->getAttachment(); @@ -8070,7 +8168,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) if (!dsqlScratch->isPsql()) dsqlScratch->flags |= DsqlCompilerScratch::FLAG_UPDATE_OR_INSERT; - const MetaName& relation_name = relation->as()->dsqlName; + const MetaName& relation_name = nodeAs(relation)->dsqlName; MetaName base_name = relation_name; bool needSavePoint; @@ -8082,7 +8180,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) insert->dsqlValues = values; insert->dsqlReturning = returning; insert->overrideClause = overrideClause; - insert = insert->internalDsqlPass(dsqlScratch, true, needSavePoint)->as(); + insert = nodeAs(insert->internalDsqlPass(dsqlScratch, true, needSavePoint)); fb_assert(insert); dsql_ctx* context = insert->dsqlRelation->dsqlContext; @@ -8154,7 +8252,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) NestConst* valuePtr = values->items.begin(); Array >& insertStatements = - insert->statement->as()->statements; + nodeAs(insert->statement)->statements; for (; fieldPtr != fieldsCopy.end(); ++fieldPtr, ++valuePtr) { @@ -8192,7 +8290,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) ++matchCount; const FB_SIZE_T fieldPos = fieldPtr - fieldsCopy.begin(); - AssignmentNode* assign2 = insertStatements[fieldPos]->as(); + AssignmentNode* assign2 = nodeAs(insertStatements[fieldPos]); NestConst& expr = assign2->asgnFrom; ValueExprNode* var = dsqlPassHiddenVariable(dsqlScratch, expr); @@ -8240,7 +8338,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) dsqlScratch, returning, insert->statement2); } - update = update->internalDsqlPass(dsqlScratch, true)->as(); + update = nodeAs(update->internalDsqlPass(dsqlScratch, true)); fb_assert(update); // test if ROW_COUNT = 0 @@ -8269,7 +8367,7 @@ StmtNode* UpdateOrInsertNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) dsqlScratch->getStatement()->setType(DsqlCompiledStatement::TYPE_INSERT); StmtNode* ret = SavepointEncloseNode::make(getPool(), dsqlScratch, list); - if (!needSavePoint || ret->is()) + if (!needSavePoint || nodeIs(ret)) return ret; return FB_NEW SavepointEncloseNode(getPool(), ret); @@ -8415,13 +8513,13 @@ static dsql_ctx* dsqlGetContext(const RecordSourceNode* node) const RelationSourceNode* relNode; const RseNode* rseNode; - if ((procNode = ExprNode::as(node))) + if ((procNode = nodeAs(node))) return procNode->dsqlContext; - if ((relNode = ExprNode::as(node))) + if ((relNode = nodeAs(node))) return relNode->dsqlContext; - if ((rseNode = ExprNode::as(node))) + if ((rseNode = nodeAs(node))) return rseNode->dsqlContext; fb_assert(false); @@ -8435,11 +8533,11 @@ static void dsqlGetContexts(DsqlContextStack& contexts, const RecordSourceNode* const RelationSourceNode* relNode; const RseNode* rseNode; - if ((procNode = ExprNode::as(node))) + if ((procNode = nodeAs(node))) contexts.push(procNode->dsqlContext); - else if ((relNode = ExprNode::as(node))) + else if ((relNode = nodeAs(node))) contexts.push(relNode->dsqlContext); - else if ((rseNode = ExprNode::as(node))) + else if ((rseNode = nodeAs(node))) { if (rseNode->dsqlContext) // derived table contexts.push(rseNode->dsqlContext); @@ -8472,11 +8570,11 @@ static StmtNode* dsqlNullifyReturning(DsqlCompilerScratch* dsqlScratch, StmtNode ModifyNode* modifyNode; StoreNode* storeNode; - if (eraseNode = input->as()) + if (eraseNode = nodeAs(input)) returning = eraseNode->statement; - else if (modifyNode = input->as()) + else if (modifyNode = nodeAs(input)) returning = modifyNode->statement2; - else if (storeNode = input->as()) + else if (storeNode = nodeAs(input)) returning = storeNode->statement2; else { @@ -8493,7 +8591,7 @@ static StmtNode* dsqlNullifyReturning(DsqlCompilerScratch* dsqlScratch, StmtNode // completely removed. // nod_returning was already processed - CompoundStmtNode* returningStmt = returning->as(); + CompoundStmtNode* returningStmt = nodeAs(returning); fb_assert(returningStmt); CompoundStmtNode* nullAssign = FB_NEW_POOL(pool) CompoundStmtNode(pool); @@ -8507,7 +8605,7 @@ static StmtNode* dsqlNullifyReturning(DsqlCompilerScratch* dsqlScratch, StmtNode { AssignmentNode* assign = FB_NEW_POOL(pool) AssignmentNode(pool); assign->asgnFrom = FB_NEW_POOL(pool) NullNode(pool); - assign->asgnTo = (*ret_ptr)->as()->asgnTo; + assign->asgnTo = nodeAs(*ret_ptr)->asgnTo; *null_ptr = assign; } @@ -8529,7 +8627,7 @@ static void dsqlFieldAppearsOnce(const Array >& values, { for (FB_SIZE_T i = 0; i < values.getCount(); ++i) { - const FieldNode* field1 = values[i]->as(); + const FieldNode* field1 = nodeAs(values[i]); if (!field1) continue; @@ -8537,7 +8635,7 @@ static void dsqlFieldAppearsOnce(const Array >& values, for (FB_SIZE_T j = i + 1; j < values.getCount(); ++j) { - const FieldNode* field2 = values[j]->as(); + const FieldNode* field2 = nodeAs(values[j]); if (!field2) continue; @@ -8589,7 +8687,7 @@ static dsql_ctx* dsqlPassCursorContext(DsqlCompilerScratch* dsqlScratch, const M DeclareCursorNode::CUR_TYPE_ALL, true); fb_assert(node); - const RseNode* nodeRse = node->rse->as(); + const RseNode* nodeRse = nodeAs(node->rse); fb_assert(nodeRse); if (nodeRse->dsqlDistinct) @@ -8607,7 +8705,7 @@ static dsql_ctx* dsqlPassCursorContext(DsqlCompilerScratch* dsqlScratch, const M for (const NestConst* const end = temp->items.end(); ptr != end; ++ptr) { RecordSourceNode* r_node = *ptr; - RelationSourceNode* relNode = ExprNode::as(r_node); + RelationSourceNode* relNode = nodeAs(r_node); if (relNode) { @@ -8628,7 +8726,7 @@ static dsql_ctx* dsqlPassCursorContext(DsqlCompilerScratch* dsqlScratch, const M context = candidate; } } - else if (ExprNode::as(r_node)) + else if (nodeAs(r_node)) { // cursor with aggregation is not updatable ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-510) << @@ -8694,8 +8792,7 @@ static RseNode* dsqlPassCursorReference(DsqlCompilerScratch* dsqlScratch, const RseNode* rse = FB_NEW_POOL(pool) RseNode(pool); rse->dsqlStreams = FB_NEW_POOL(pool) RecSourceListNode(pool, 1); - RelationSourceNode* relation_node = - PASS1_relation(dsqlScratch, relation_name)->as(); + RelationSourceNode* relation_node = nodeAs(PASS1_relation(dsqlScratch, relation_name)); rse->dsqlStreams->items[0] = relation_node; RecordKeyNode* dbKeyNode = FB_NEW_POOL(pool) RecordKeyNode(pool, blr_dbkey); @@ -8961,7 +9058,7 @@ static void dsqlSetParameterName(ExprNode* exprNode, const ValueExprNode* fld_no if (!exprNode) return; - const FieldNode* fieldNode = ExprNode::as(fld_node); + const FieldNode* fieldNode = nodeAs(fld_node); fb_assert(fieldNode); // Could it be something else ??? if (fieldNode->nodDesc.dsc_dtype != dtype_array) @@ -8984,7 +9081,7 @@ static void dsqlSetParameterName(ExprNode* exprNode, const ValueExprNode* fld_no case ExprNode::TYPE_PARAMETER: { - ParameterNode* paramNode = exprNode->as(); + ParameterNode* paramNode = nodeAs(exprNode); dsql_par* parameter = paramNode->dsqlParameter; parameter->par_name = fieldNode->dsqlField->fld_name.c_str(); parameter->par_rel_name = relation->rel_name.c_str(); @@ -9005,7 +9102,7 @@ static void dsqlSetParametersName(CompoundStmtNode* statements, const RecordSour for (NestConst* const end = ptr + count; ptr != end; ++ptr) { - AssignmentNode* assign = (*ptr)->as(); + AssignmentNode* assign = nodeAs(*ptr); if (assign) dsqlSetParameterName(assign->asgnFrom, assign->asgnTo, relation); @@ -9154,7 +9251,7 @@ static StmtNode* pass1ExpandView(thread_db* tdbb, CompilerScratch* csb, StreamTy const jrd_fld* field = MET_get_field(relation, id); if (field->fld_source) - newId = field->fld_source->as()->fieldId; + newId = nodeAs(field->fld_source)->fieldId; else newId = id; } @@ -9353,13 +9450,13 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, for (size_t i = compoundNode->statements.getCount(); i--; ) { - const AssignmentNode* assign = compoundNode->statements[i]->as(); + const AssignmentNode* assign = nodeAs(compoundNode->statements[i]); fb_assert(assign); if (!assign) continue; const ExprNode* assignFrom = assign->asgnFrom; - const FieldNode* assignToField = assign->asgnTo->as(); + const FieldNode* assignToField = nodeAs(assign->asgnTo); if (assignToField) { @@ -9374,7 +9471,7 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, { if (insertOverride && fld->fld_identity_type.specified) { - if (insertOverride->specified || !assignFrom->is()) + if (insertOverride->specified || !nodeIs(assignFrom)) identityType = fld->fld_identity_type; if (*insertOverride == OverrideClause::USER_VALUE) @@ -9386,7 +9483,7 @@ static void preprocessAssignments(thread_db* tdbb, CompilerScratch* csb, if (fld->fld_computation) { - if (assignFrom->is()) + if (nodeIs(assignFrom)) compoundNode->statements.remove(i); } else if (relation->rel_view_rse && fld->fld_source_rel_field.first.hasData()) @@ -9447,7 +9544,7 @@ static void validateExpressions(thread_db* tdbb, const Array& vali const dsc* desc = EVL_expr(tdbb, request, i->value); const USHORT length = (desc && !(request->req_flags & req_null)) ? - MOV_make_string(desc, ttype_dynamic, &value, &temp, sizeof(temp) - 1) : 0; + MOV_make_string(tdbb, desc, ttype_dynamic, &value, &temp, sizeof(temp) - 1) : 0; if (!desc || (request->req_flags & req_null)) value = NULL_STRING_MARK; @@ -9457,7 +9554,7 @@ static void validateExpressions(thread_db* tdbb, const Array& vali const_cast(value)[length] = 0; // safe cast - data is actually on the stack string name; - const FieldNode* fieldNode = i->value->as(); + const FieldNode* fieldNode = nodeAs(i->value); if (fieldNode) { diff --git a/src/dsql/StmtNodes.h b/src/dsql/StmtNodes.h index 57329a08e0..60c0d1f475 100644 --- a/src/dsql/StmtNodes.h +++ b/src/dsql/StmtNodes.h @@ -1556,22 +1556,18 @@ public: }; -// This node should better be session management node, -// but as long as we do not have other session management and -// node is rather similiar internally to transaction management -// let it for now be transaction management node. -class SetRoleNode : public TransactionNode +class SetRoleNode : public SessionManagementNode { public: explicit SetRoleNode(MemoryPool& pool) - : TransactionNode(pool), + : SessionManagementNode(pool), trusted(true), roleName(pool) { } SetRoleNode(MemoryPool& pool, Firebird::MetaName* name) - : TransactionNode(pool), + : SessionManagementNode(pool), trusted(false), roleName(pool, *name) { @@ -1580,7 +1576,7 @@ public: public: virtual Firebird::string internalPrint(NodePrinter& printer) const { - TransactionNode::internalPrint(printer); + SessionManagementNode::internalPrint(printer); NODE_PRINT(printer, trusted); NODE_PRINT(printer, roleName); @@ -1588,8 +1584,7 @@ public: return "SetRoleNode"; } - virtual SetRoleNode* dsqlPass(DsqlCompilerScratch* dsqlScratch); - virtual void execute(thread_db* tdbb, dsql_req* request, jrd_tra** transaction) const; + virtual void execute(thread_db* tdbb, dsql_req* request) const; public: bool trusted; @@ -1597,6 +1592,99 @@ public: }; +class SetSessionNode : public SessionManagementNode +{ +public: + enum Type { TYPE_IDLE_TIMEOUT, TYPE_STMT_TIMEOUT }; + + SetSessionNode(MemoryPool& pool, Type aType, ULONG aVal, UCHAR blr_timepart); + +public: + virtual Firebird::string internalPrint(NodePrinter& printer) const; + virtual void execute(thread_db* tdbb, dsql_req* request) const; + +private: + Type m_type; + ULONG m_value; +}; + + +class SetRoundNode : public SessionManagementNode +{ +public: + SetRoundNode(MemoryPool& pool, Firebird::MetaName* name); + +public: + virtual Firebird::string internalPrint(NodePrinter& printer) const + { + SessionManagementNode::internalPrint(printer); + + NODE_PRINT(printer, rndMode); + + return "SetRoundNode"; + } + + virtual void execute(thread_db* tdbb, dsql_req* request) const; + +public: + USHORT rndMode; +}; + + +class SetTrapsNode : public SessionManagementNode +{ +public: + SetTrapsNode(MemoryPool& pool) + : SessionManagementNode(pool), + traps(0u) + { + } + +public: + virtual Firebird::string internalPrint(NodePrinter& printer) const + { + SessionManagementNode::internalPrint(printer); + + NODE_PRINT(printer, traps); + + return "SetTrapsNode"; + } + + virtual void execute(thread_db* tdbb, dsql_req* request) const; + + void trap(Firebird::MetaName* name); + +public: + USHORT traps; +}; + + +class SetBindNode : public SessionManagementNode +{ +public: + SetBindNode(MemoryPool& pool) + : SessionManagementNode(pool) + { + } + +public: + virtual Firebird::string internalPrint(NodePrinter& printer) const + { + SessionManagementNode::internalPrint(printer); + + NODE_PRINT(printer, bind.bind); + NODE_PRINT(printer, bind.numScale); + + return "SetBindNode"; + } + + virtual void execute(thread_db* tdbb, dsql_req* request) const; + +public: + Firebird::DecimalBinding bind; +}; + + class UpdateOrInsertNode : public TypedNode { public: diff --git a/src/dsql/WinNodes.cpp b/src/dsql/WinNodes.cpp index 2766f16aed..e04c452792 100644 --- a/src/dsql/WinNodes.cpp +++ b/src/dsql/WinNodes.cpp @@ -599,7 +599,7 @@ dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* if (!desc || (request->req_flags & req_null)) return NULL; - SINT64 records = MOV_get_int64(desc, 0); + SINT64 records = MOV_get_int64(tdbb, desc, 0); if (records <= 0) { status_exception::raise(Arg::Gds(isc_sysf_argnmustbe_positive) << @@ -607,7 +607,7 @@ dsc* NthValueWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* } desc = EVL_expr(tdbb, request, from); - const SLONG fromPos = desc ? MOV_get_long(desc, 0) : FROM_FIRST; + const SLONG fromPos = desc ? MOV_get_long(tdbb, desc, 0) : FROM_FIRST; if (fromPos == FROM_FIRST) records += -(window->getRecordPosition() - window->getFrameStart()) - 1; @@ -688,7 +688,7 @@ dsc* LagLeadWinNode::winPass(thread_db* tdbb, jrd_req* request, SlidingWindow* w if (!desc || (request->req_flags & req_null)) return NULL; - SINT64 records = MOV_get_int64(desc, 0); + SINT64 records = MOV_get_int64(tdbb, desc, 0); if (records < 0) { status_exception::raise(Arg::Gds(isc_sysf_argnmustbe_nonneg) << @@ -850,7 +850,7 @@ void NTileWinNode::aggInit(thread_db* tdbb, jrd_req* request) const Arg::Num(1) << Arg::Str(aggInfo.name)); } - thisImpure->buckets = MOV_get_int64(desc, 0); + thisImpure->buckets = MOV_get_int64(tdbb, desc, 0); if (thisImpure->buckets <= 0) { diff --git a/src/dsql/ddl_proto.h b/src/dsql/ddl_proto.h index 5f93c1e934..206c0e1d09 100644 --- a/src/dsql/ddl_proto.h +++ b/src/dsql/ddl_proto.h @@ -57,7 +57,9 @@ const USHORT blr_dtypes[] = { blr_short, // dtype_array blr_int64, // dtype_int64 0, // DB_KEY - blr_bool // dtype_boolean + blr_bool, // dtype_boolean + blr_dec64, // dtype_dec64 + blr_dec128 // dtype_dec128 }; bool DDL_ids(const Jrd::DsqlCompilerScratch*); diff --git a/src/dsql/dsql.cpp b/src/dsql/dsql.cpp index 2be813f0bb..21c8de18a4 100644 --- a/src/dsql/dsql.cpp +++ b/src/dsql/dsql.cpp @@ -151,7 +151,7 @@ void DSQL_execute(thread_db* tdbb, if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SET_SESSION) + statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); @@ -548,7 +548,7 @@ void DSQL_execute_immediate(thread_db* tdbb, Jrd::Attachment* attachment, jrd_tr if (!*tra_handle && statement->getType() != DsqlCompiledStatement::TYPE_START_TRANS && - statement->getType() != DsqlCompiledStatement::TYPE_SET_SESSION) + statement->getType() != DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-901) << Arg::Gds(isc_bad_trans_handle)); @@ -922,15 +922,7 @@ void DsqlTransactionRequest::execute(thread_db* tdbb, jrd_tra** traHandle, } -void SetSessionRequest::execute(thread_db* tdbb, jrd_tra** /*traHandle*/, - IMessageMetadata* /*inMetadata*/, const UCHAR* /*inMsg*/, - IMessageMetadata* /*outMetadata*/, UCHAR* /*outMsg*/, - bool /*singleton*/) -{ - node->execute(tdbb, this); -} - -void SetSessionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, +void DsqlSessionManagementRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, ntrace_result_t* /*traceResult*/) { node = Node::doDsqlPass(scratch, node); @@ -939,6 +931,15 @@ void SetSessionRequest::dsqlPass(thread_db* tdbb, DsqlCompilerScratch* scratch, req_traced = false; } +// Execute a dynamic SQL statement. +void DsqlSessionManagementRequest::execute(thread_db* tdbb, jrd_tra** traHandle, + Firebird::IMessageMetadata* inMetadata, const UCHAR* inMsg, + Firebird::IMessageMetadata* outMetadata, UCHAR* outMsg, + bool singleton) +{ + node->execute(tdbb, this); +} + /** @@ -1875,8 +1876,6 @@ static void sql_info(thread_db* tdbb, break; case DsqlCompiledStatement::TYPE_CREATE_DB: case DsqlCompiledStatement::TYPE_DDL: - case DsqlCompiledStatement::TYPE_SET_ROLE: - case DsqlCompiledStatement::TYPE_SET_SESSION: number = isc_info_sql_stmt_ddl; break; case DsqlCompiledStatement::TYPE_COMMIT: @@ -1890,6 +1889,9 @@ static void sql_info(thread_db* tdbb, case DsqlCompiledStatement::TYPE_START_TRANS: number = isc_info_sql_stmt_start_trans; break; + case DsqlCompiledStatement::TYPE_SESSION_MANAGEMENT: + number = isc_info_sql_stmt_ddl; // ????????????????? + break; case DsqlCompiledStatement::TYPE_INSERT: number = isc_info_sql_stmt_insert; break; diff --git a/src/dsql/dsql.h b/src/dsql/dsql.h index 53c03c9e41..21ce846320 100644 --- a/src/dsql/dsql.h +++ b/src/dsql/dsql.h @@ -80,7 +80,7 @@ namespace Jrd class RseNode; class StmtNode; class TransactionNode; - class SetSessionNode; + class SessionManagementNode; class ValueExprNode; class ValueListNode; class WindowClause; @@ -237,6 +237,31 @@ public: { } +public: + void setExactPrecision() + { + if (precision != 0) + return; + + switch (dtype) + { + case dtype_short: + precision = 4; + break; + + case dtype_long: + precision = 9; + break; + + case dtype_int64: + precision = 18; + break; + + default: + fb_assert(!DTYPE_IS_EXACT(dtype)); + } + } + public: USHORT dtype; FLD_LENGTH length; @@ -433,7 +458,7 @@ public: TYPE_SELECT, TYPE_SELECT_UPD, TYPE_INSERT, TYPE_DELETE, TYPE_UPDATE, TYPE_UPDATE_CURSOR, TYPE_DELETE_CURSOR, TYPE_COMMIT, TYPE_ROLLBACK, TYPE_CREATE_DB, TYPE_DDL, TYPE_START_TRANS, TYPE_EXEC_PROCEDURE, TYPE_COMMIT_RETAIN, TYPE_ROLLBACK_RETAIN, TYPE_SET_GENERATOR, - TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SET_ROLE, TYPE_SET_SESSION + TYPE_SAVEPOINT, TYPE_EXEC_BLOCK, TYPE_SELECT_BLOCK, TYPE_SESSION_MANAGEMENT }; // Statement flags. @@ -686,10 +711,10 @@ private: NestConst node; }; -class SetSessionRequest : public dsql_req +class DsqlSessionManagementRequest : public dsql_req { public: - explicit SetSessionRequest(MemoryPool& pool, SetSessionNode* aNode) + explicit DsqlSessionManagementRequest(MemoryPool& pool, SessionManagementNode* aNode) : dsql_req(pool), node(aNode) { @@ -705,7 +730,7 @@ public: bool singleton); private: - NestConst node; + NestConst node; }; //! Implicit (NATURAL and USING) joins @@ -958,6 +983,23 @@ private: Firebird::string s; }; +class Lim64String : public Firebird::string +{ +public: + Lim64String(Firebird::MemoryPool& p, const Firebird::string& str, int sc) + : Firebird::string(p, str), + scale(sc) + { } + + int getScale() + { + return scale; + } + +private: + int scale; +}; + } // namespace /*! \var unsigned DSQL_debug diff --git a/src/dsql/gen.cpp b/src/dsql/gen.cpp index b68977a00a..cde7b3a118 100644 --- a/src/dsql/gen.cpp +++ b/src/dsql/gen.cpp @@ -108,7 +108,7 @@ void GEN_hidden_variables(DsqlCompilerScratch* dsqlScratch) **/ void GEN_expr(DsqlCompilerScratch* dsqlScratch, ExprNode* node) { - RseNode* rseNode = node->as(); + RseNode* rseNode = nodeAs(node); if (rseNode) { GEN_rse(dsqlScratch, rseNode); @@ -201,6 +201,25 @@ void GEN_port(DsqlCompilerScratch* dsqlScratch, dsql_msg* message) if (fromCharSet != toCharSet) parameter->par_desc.setTextType(toCharSet); } + else if (parameter->par_desc.isDecFloat()) + { + const DecimalBinding& b = tdbb->getAttachment()->att_dec_binding; + switch (b.bind) + { + case DecimalBinding::DEC_NATIVE: + break; + case DecimalBinding::DEC_TEXT: + parameter->par_desc.makeText((parameter->par_desc.dsc_dtype == dtype_dec64 ? + IDecFloat16::STRING_SIZE : IDecFloat34::STRING_SIZE) - 1, ttype_ascii); + break; + case DecimalBinding::DEC_DOUBLE: + parameter->par_desc.makeDouble(); + break; + case DecimalBinding::DEC_NUMERIC: + parameter->par_desc.makeInt64(b.numScale); + break; + } + } if (parameter->par_desc.dsc_dtype == dtype_text && parameter->par_index != 0) { @@ -366,6 +385,14 @@ void GEN_descriptor( DsqlCompilerScratch* dsqlScratch, const dsc* desc, bool tex dsqlScratch->appendUChar(blr_double); break; + case dtype_dec64: + dsqlScratch->appendUChar(blr_dec64); + break; + + case dtype_dec128: + dsqlScratch->appendUChar(blr_dec128); + break; + case dtype_sql_date: dsqlScratch->appendUChar(blr_sql_date); break; @@ -598,7 +625,7 @@ void GEN_sort(DsqlCompilerScratch* dsqlScratch, UCHAR blrVerb, ValueListNode* li for (const NestConst* const end = list->items.end(); ptr != end; ++ptr) { - OrderNode* orderNode = (*ptr)->as(); + OrderNode* orderNode = nodeAs(*ptr); switch (orderNode->nullsPlacement) { diff --git a/src/dsql/make.cpp b/src/dsql/make.cpp index 29a7ea633e..a41c22ef14 100644 --- a/src/dsql/make.cpp +++ b/src/dsql/make.cpp @@ -118,19 +118,25 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag) switch (numeric_flag) { case CONSTANT_DOUBLE: + case CONSTANT_DECIMAL: // This is a numeric value which is transported to the engine as - // a string. The engine will convert it. Use dtype_double so that - // the engine can distinguish it from an actual string. - // Note: Due to the size of dsc_scale we are limited to numeric - // constants of less than 256 bytes. + // a string. The engine will convert it. Use dtype_double/dec128 + // so that the engine can distinguish it from an actual string. + // Note: Due to the size of dsc_sub_type literal length is limited + // to constants less than 32K - 1 bytes. Not real problem. - literal->litDesc.dsc_dtype = dtype_double; - // Scale has no use for double - literal->litDesc.dsc_scale = static_cast(strlen(str)); - literal->litDesc.dsc_sub_type = 0; - literal->litDesc.dsc_length = sizeof(double); - literal->litDesc.dsc_address = (UCHAR*) str; - literal->litDesc.dsc_ttype() = ttype_ascii; + { + literal->litDesc.dsc_dtype = numeric_flag == CONSTANT_DOUBLE ? dtype_double : dtype_dec128; + literal->litDesc.dsc_scale = 0; + size_t l = strlen(str); + if (l > MAX_SSHORT) + { + ERRD_post(Arg::Gds(isc_imp_exc) << Arg::Gds(isc_random) << "Numeric literal too long"); + } + literal->litDesc.dsc_sub_type = static_cast(l); // Keep length in sub_type which is unused + literal->litDesc.dsc_length = numeric_flag == CONSTANT_DOUBLE ? sizeof(double) : sizeof(Decimal128); + literal->litDesc.dsc_address = (UCHAR*) str; + } break; case CONSTANT_DATE: @@ -169,7 +175,7 @@ ValueExprNode* MAKE_constant(const char* str, dsql_constant_type numeric_flag) // Now invoke the string_to_date/time/timestamp routines - CVT_move(&tmp, &literal->litDesc, ERRD_post); + CVT_move(&tmp, &literal->litDesc, tdbb->getAttachment()->att_dec_status, ERRD_post); break; } diff --git a/src/dsql/make_proto.h b/src/dsql/make_proto.h index f14db35302..7e6311c467 100644 --- a/src/dsql/make_proto.h +++ b/src/dsql/make_proto.h @@ -46,6 +46,7 @@ namespace Jrd { // Parameters to MAKE_constant enum dsql_constant_type { CONSTANT_DOUBLE = 1, // stored as a string + CONSTANT_DECIMAL, // stored as a string CONSTANT_DATE, // stored as a SLONG CONSTANT_TIME, // stored as a ULONG CONSTANT_TIMESTAMP, // stored as a QUAD diff --git a/src/dsql/parse.y b/src/dsql/parse.y index 214515b423..723b7c2471 100644 --- a/src/dsql/parse.y +++ b/src/dsql/parse.y @@ -332,7 +332,8 @@ using namespace Firebird; %token WORK %token WRITE -%token FLOAT_NUMBER +%token FLOAT_NUMBER DECIMAL_NUMBER LIMIT64_INT +%token LIMIT64_NUMBER %token SYMBOL %token NUMBER @@ -592,19 +593,25 @@ using namespace Firebird; // tokens added for Firebird 4.0 %token BINARY +%token BIND +%token COMPARE_DECFLOAT %token CUME_DIST +%token DECFLOAT %token DEFINER %token EXCLUDE %token FOLLOWING %token IDLE %token INVOKER %token MESSAGE +%token NATIVE +%token NORMALIZE_DECFLOAT %token NTILE %token OTHERS %token OVERRIDING %token PERCENT_RANK %token PRECEDING %token PRIVILEGE +%token QUANTIZE %token RANGE %token RDB_ERROR %token RDB_ROLE_IN_USE @@ -614,6 +621,8 @@ using namespace Firebird; %token SQL %token SYSTEM %token TIES +%token TOTALORDER +%token TRAPS %token UNBOUNDED %token VARBINARY %token WINDOW @@ -673,6 +682,7 @@ using namespace Firebird; Firebird::QualifiedName* qualifiedNamePtr; Firebird::string* stringPtr; Jrd::IntlString* intlStringPtr; + Jrd::Lim64String* lim64ptr; Jrd::DbFileClause* dbFileClause; Firebird::Array >* dbFilesClause; Jrd::ExternalClause* externalClause; @@ -704,6 +714,7 @@ using namespace Firebird; Jrd::NamedWindowClause* namedWindowClause; Jrd::NamedWindowsClause* namedWindowsClause; Jrd::TransactionNode* traNode; + Jrd::SessionManagementNode* mngNode; Firebird::Array* privilegeArray; Jrd::GranteeClause* granteeClause; Firebird::Array* granteeArray; @@ -762,6 +773,9 @@ using namespace Firebird; Jrd::SetRoleNode* setRoleNode; Jrd::SetSessionNode* setSessionNode; Jrd::CreateAlterRoleNode* createAlterRoleNode; + Jrd::SetRoundNode* setRoundNode; + Jrd::SetTrapsNode* setTrapsNode; + Jrd::SetBindNode* setBindNode; } %include types.y @@ -780,7 +794,7 @@ statement : dml_statement { $$ = newNode($1); } | ddl_statement { $$ = newNode($1); } | tra_statement { $$ = newNode($1); } - | session_statement { $$ = newNode($1); } + | mng_statement { $$ = newNode($1); } ; %type dml_statement @@ -815,6 +829,14 @@ tra_statement : set_transaction { $$ = $1; } | commit { $$ = $1; } | rollback { $$ = $1; } + ; + +%type mng_statement +mng_statement + : set_round { $$ = $1; } + | set_traps { $$ = $1; } + | set_bind { $$ = $1; } + | session_statement { $$ = $1; } | set_role { $$ = $1; } ; @@ -1826,6 +1848,10 @@ sequence_value $$ = -signedNumber; } + | '-' LIMIT64_INT + { + $$ = MIN_SINT64; + } ; @@ -2349,7 +2375,7 @@ column_constraint($addColumnClause) const NestConst* ptr = refColumns->items.begin(); for (const NestConst* const end = refColumns->items.end(); ptr != end; ++ptr) - constraint.refColumns.add((*ptr)->as()->dsqlName); + constraint.refColumns.add(nodeAs(*ptr)->dsqlName); } constraint.index = $5; @@ -2400,7 +2426,7 @@ table_constraint($relationNode) const NestConst* ptr = columns->items.begin(); for (const NestConst* const end = columns->items.end(); ptr != end; ++ptr) - constraint.columns.add((*ptr)->as()->dsqlName); + constraint.columns.add(nodeAs(*ptr)->dsqlName); constraint.index = $3; @@ -2415,7 +2441,7 @@ table_constraint($relationNode) const NestConst* ptr = columns->items.begin(); for (const NestConst* const end = columns->items.end(); ptr != end; ++ptr) - constraint.columns.add((*ptr)->as()->dsqlName); + constraint.columns.add(nodeAs(*ptr)->dsqlName); constraint.index = $4; @@ -2431,7 +2457,7 @@ table_constraint($relationNode) const NestConst* ptr = columns->items.begin(); for (const NestConst* const end = columns->items.end(); ptr != end; ++ptr) - constraint.columns.add((*ptr)->as()->dsqlName); + constraint.columns.add(nodeAs(*ptr)->dsqlName); constraint.refRelation = *$5; constraint.refAction = $7; @@ -2442,7 +2468,7 @@ table_constraint($relationNode) const NestConst* ptr = refColumns->items.begin(); for (const NestConst* const end = refColumns->items.end(); ptr != end; ++ptr) - constraint.refColumns.add((*ptr)->as()->dsqlName); + constraint.refColumns.add(nodeAs(*ptr)->dsqlName); } constraint.index = $8; @@ -3572,78 +3598,45 @@ check_opt %type trigger_clause trigger_clause - : symbol_trigger_name trigger_active trigger_type trigger_position trg_sql_security_clause - AS local_declaration_list full_proc_block + : create_trigger_start trg_sql_security_clause AS local_declaration_list full_proc_block { - $$ = newNode(*$1); - $$->active = $2; - $$->type = $3; - $$->position = $4; - $$->ssDefiner = $5; - $$->source = makeParseStr(YYPOSNARG(6), YYPOSNARG(8)); - $$->localDeclList = $7; - $$->body = $8; + $$ = $1; + $$->ssDefiner = $2; + $$->source = makeParseStr(YYPOSNARG(3), YYPOSNARG(5)); + $$->localDeclList = $4; + $$->body = $5; } - | symbol_trigger_name trigger_active trigger_type trigger_position - external_clause external_body_clause_opt + | create_trigger_start external_clause external_body_clause_opt { - $$ = newNode(*$1); - $$->active = $2; - $$->type = $3; - $$->position = $4; - $$->external = $5; - if ($6) - $$->source = *$6; + $$ = $1; + $$->external = $2; + if ($3) + $$->source = *$3; } - | symbol_trigger_name trigger_active trigger_type trigger_position ON symbol_table_name trg_sql_security_clause - AS local_declaration_list full_proc_block + ; + +%type create_trigger_start +create_trigger_start + : symbol_trigger_name + { $$ = newNode(*$1); } + create_trigger_common(NOTRIAL($2)) + { $$ = $2; } + ; + +%type create_trigger_common() +create_trigger_common($trigger) + : trigger_active trigger_type(NOTRIAL($trigger)) trigger_position { - $$ = newNode(*$1); - $$->active = $2; - $$->type = $3; - $$->position = $4; - $$->relationName = *$6; - $$->ssDefiner = $7; - $$->source = makeParseStr(YYPOSNARG(8), YYPOSNARG(10)); - $$->localDeclList = $9; - $$->body = $10; + $trigger->active = $1; + $trigger->type = $2; + $trigger->position = $3; } - | symbol_trigger_name trigger_active trigger_type trigger_position ON symbol_table_name - external_clause external_body_clause_opt + | FOR symbol_table_name trigger_active table_trigger_type trigger_position { - $$ = newNode(*$1); - $$->active = $2; - $$->type = $3; - $$->position = $4; - $$->relationName = *$6; - $$->external = $7; - if ($8) - $$->source = *$8; - } - | symbol_trigger_name FOR symbol_table_name trigger_active trigger_type trigger_position trg_sql_security_clause - AS local_declaration_list full_proc_block - { - $$ = newNode(*$1); - $$->active = $4; - $$->type = $5; - $$->position = $6; - $$->relationName = *$3; - $$->ssDefiner = $7; - $$->source = makeParseStr(YYPOSNARG(8), YYPOSNARG(10)); - $$->localDeclList = $9; - $$->body = $10; - } - | symbol_trigger_name FOR symbol_table_name trigger_active trigger_type trigger_position - external_clause external_body_clause_opt - { - $$ = newNode(*$1); - $$->active = $4; - $$->type = $5; - $$->position = $6; - $$->relationName = *$3; - $$->external = $7; - if ($8) - $$->source = *$8; + $trigger->relationName = *$2; + $trigger->active = $3; + $trigger->type = $4; + $trigger->position = $5; } ; @@ -3666,11 +3659,22 @@ trigger_active { $$ = Nullable::empty(); } ; -%type trigger_type -trigger_type +%type trigger_type() +trigger_type($trigger) + : table_trigger_type ON symbol_table_name + { + $$ = $1; + $trigger->relationName = *$3; + } + | ON trigger_db_type + { $$ = $2; } + | trigger_type_prefix trigger_ddl_type + { $$ = $1 + $2; } + ; + +%type table_trigger_type +table_trigger_type : trigger_type_prefix trigger_type_suffix { $$ = $1 + $2 - 1; } - | ON trigger_db_type { $$ = $2; } - | trigger_type_prefix trigger_ddl_type { $$ = $1 + $2; } ; %type trigger_db_type @@ -4466,6 +4470,7 @@ non_charset_simple_type | binary_character_type | numeric_type | float_type + | decfloat_type | BIGINT { $$ = newNode(); @@ -4733,6 +4738,20 @@ varbinary_character_keyword // numeric type +%type decfloat_type +decfloat_type + : DECFLOAT '(' signed_long_integer ')' + { + if ($3 != 16 && $3 != 34) + yyabandon(YYPOSNARG(3), -842, isc_decprecision_err); // DecFloat precision must be 16 or 34. + + $$ = newNode(); + $$->precision = $3; + $$->dtype = $3 == 16 ? dtype_dec64 : dtype_dec128; + $$->length = $3 == 16 ? sizeof(Decimal64) : sizeof(Decimal128); + } + ; + %type numeric_type numeric_type : NUMERIC prec_scale @@ -5017,6 +5036,68 @@ set_role { $$ = newNode(); } ; +%type set_round +set_round + : SET DECFLOAT ROUND valid_symbol_name + { $$ = newNode($4); } + ; + +%type set_traps +set_traps + : SET DECFLOAT TRAPS TO + { $$ = newNode(); } + traps_list_opt($5) + { $$ = $5; } + ; + +%type set_bind +set_bind + : SET DECFLOAT BIND + { $$ = newNode(); } + bind_clause($4) + { $$ = $4; } + ; + +%type traps_list_opt() +traps_list_opt($setTrapsNode) + : // nothing + | traps_list($setTrapsNode) + ; + +%type traps_list() +traps_list($setTrapsNode) + : trap($setTrapsNode) + | traps_list ',' trap($setTrapsNode) + ; + +%type trap() +trap($setTrapsNode) + : valid_symbol_name + { $setTrapsNode->trap($1); } + ; + +%type bind_clause() +bind_clause($setBindNode) + : NATIVE + // do nothing + | character_keyword + { $setBindNode->bind.bind = DecimalBinding::DEC_TEXT; } + | DOUBLE PRECISION + { $setBindNode->bind.bind = DecimalBinding::DEC_DOUBLE; } + | BIGINT scale_clause($setBindNode) + { $setBindNode->bind.bind = DecimalBinding::DEC_NUMERIC; } + ; + +%type scale_clause() +scale_clause($setBindNode) + : // nothing + | ',' signed_long_integer + { + if ($2 > 18 || $2 < 0) + yyabandon(YYPOSNARG(2), -842, isc_scale_nogt); // Scale must be between 0 and precision + $setBindNode->bind.numScale = -$2; + } + %type session_statement session_statement : SET SESSION IDLE TIMEOUT long_integer timepart_sesion_idle_tout @@ -5380,7 +5461,7 @@ select_expr_body { $$ = $1; } | select_expr_body UNION distinct_noise query_term { - UnionSourceNode* node = $1->as(); + UnionSourceNode* node = nodeAs($1); if (node && !node->dsqlAll) node->dsqlClauses->add($4); else @@ -5392,7 +5473,7 @@ select_expr_body } | select_expr_body UNION ALL query_term { - UnionSourceNode* node = $1->as(); + UnionSourceNode* node = nodeAs($1); if (node && node->dsqlAll) node->dsqlClauses->add($4); else @@ -7044,16 +7125,30 @@ value_list %type constant constant : u_constant - | '-' u_numeric_constant { $$ = newNode($2); } + | '-' ul_numeric_constant { $$ = newNode($2); } + | '-' LIMIT64_INT { $$ = MAKE_const_sint64(MIN_SINT64, 0); } + | '-' LIMIT64_NUMBER { $$ = MAKE_const_sint64(MIN_SINT64, $2->getScale()); } | boolean_literal ; %type u_numeric_constant u_numeric_constant + : ul_numeric_constant + { $$ = $1; } + | LIMIT64_NUMBER + { $$ = MAKE_constant($1->c_str(), CONSTANT_DECIMAL); } + | LIMIT64_INT + { $$ = MAKE_constant($1->c_str(), CONSTANT_DECIMAL); } + ; + +%type ul_numeric_constant +ul_numeric_constant : NUMBER { $$ = MAKE_const_slong($1); } | FLOAT_NUMBER { $$ = MAKE_constant($1->c_str(), CONSTANT_DOUBLE); } + | DECIMAL_NUMBER + { $$ = MAKE_constant($1->c_str(), CONSTANT_DECIMAL); } | NUMBER64BIT { SINT64 signedNumber = (SINT64) $1.number; @@ -7586,6 +7681,10 @@ system_function_std_syntax | TANH | TRUNC | UUID_TO_CHAR + | QUANTIZE + | TOTALORDER + | NORMALIZE_DECFLOAT + | COMPARE_DECFLOAT ; %type system_function_special_syntax @@ -7808,10 +7907,10 @@ searched_case ValueIfNode* last = $2; ValueIfNode* next; - while ((next = last->falseValue->as())) + while ((next = nodeAs(last->falseValue))) last = next; - fb_assert(last->falseValue->is()); + fb_assert(nodeIs(last->falseValue)); last->falseValue = $4; $$ = $2; @@ -7828,10 +7927,10 @@ searched_when_clause ValueIfNode* last = $1; ValueIfNode* next; - while ((next = last->falseValue->as())) + while ((next = nodeAs(last->falseValue))) last = next; - fb_assert(last->falseValue->is()); + fb_assert(nodeIs(last->falseValue)); last->falseValue = cond; $$ = $1; @@ -8284,25 +8383,33 @@ non_reserved_word | SERVERWIDE | INCREMENT | TRUSTED - | CUME_DIST // added in FB 4.0 + | BIND // added in FB 4.0 + | COMPARE_DECFLOAT + | CUME_DIST + | DECFLOAT | DEFINER | EXCLUDE | FOLLOWING | IDLE | INVOKER | MESSAGE + | NATIVE + | NORMALIZE_DECFLOAT | NTILE | OTHERS | OVERRIDING | PERCENT_RANK | PRECEDING | PRIVILEGE + | QUANTIZE | RANGE | SECURITY | SESSION | SQL | SYSTEM | TIES + | TOTALORDER + | TRAPS | CONSISTENCY ; diff --git a/src/dsql/pass1.cpp b/src/dsql/pass1.cpp index a2f330aae3..74ed71b620 100644 --- a/src/dsql/pass1.cpp +++ b/src/dsql/pass1.cpp @@ -351,11 +351,11 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* RelationSourceNode* relNode = NULL; SelectExprNode* selNode = NULL; - if ((procNode = relationNode->as())) + if ((procNode = nodeAs(relationNode))) relation_name = procNode->dsqlName.identifier; - else if ((relNode = relationNode->as())) + else if ((relNode = nodeAs(relationNode))) relation_name = relNode->dsqlName; - else if ((selNode = relationNode->as())) + else if ((selNode = nodeAs(relationNode))) relation_name = selNode->alias.c_str(); SelectExprNode* cte = NULL; @@ -445,11 +445,11 @@ dsql_ctx* PASS1_make_context(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* // find the context alias name, if it exists. string str; - if ((procNode = relationNode->as())) + if ((procNode = nodeAs(relationNode))) str = procNode->alias; - else if ((relNode = relationNode->as())) + else if ((relNode = nodeAs(relationNode))) str = relNode->alias; - else if ((selNode = relationNode->as())) + else if ((selNode = nodeAs(relationNode))) { str = selNode->alias; // ASF: In the case of a UNION contained in a CTE, selNode->querySpec will be a @@ -668,11 +668,11 @@ void PASS1_check_unique_fields_names(StrArray& names, const CompoundStmtNode* fi const DeclareVariableNode* varNode; const DeclareCursorNode* cursorNode; - if ((varNode = (*ptr)->as())) + if ((varNode = nodeAs(*ptr))) name = varNode->dsqlDef->name.c_str(); - else if ((cursorNode = (*ptr)->as())) + else if ((cursorNode = nodeAs(*ptr))) name = cursorNode->dsqlName.c_str(); - else if ((*ptr)->as() || (*ptr)->as()) + else if (nodeAs(*ptr) || nodeAs(*ptr)) continue; fb_assert(name); @@ -796,11 +796,11 @@ bool PASS1_node_match(const ExprNode* node1, const ExprNode* node2, bool ignoreM if (!node1 || !node2) return false; - const CastNode* castNode1 = node1->as(); + const CastNode* castNode1 = nodeAs(node1); if (ignoreMapCast && castNode1) { - const CastNode* castNode2 = node2->as(); + const CastNode* castNode2 = nodeAs(node2); // If node2 is also cast and same type continue with both sources. if (castNode2 && @@ -815,11 +815,11 @@ bool PASS1_node_match(const ExprNode* node1, const ExprNode* node2, bool ignoreM return PASS1_node_match(castNode1->source, node2, ignoreMapCast); } - const DsqlMapNode* mapNode1 = node1->as(); + const DsqlMapNode* mapNode1 = nodeAs(node1); if (ignoreMapCast && mapNode1) { - const DsqlMapNode* mapNode2 = node2->as(); + const DsqlMapNode* mapNode2 = nodeAs(node2); if (mapNode2) { @@ -832,8 +832,8 @@ bool PASS1_node_match(const ExprNode* node1, const ExprNode* node2, bool ignoreM return PASS1_node_match(mapNode1->map->map_node, node2, ignoreMapCast); } - const DsqlAliasNode* aliasNode1 = node1->as(); - const DsqlAliasNode* aliasNode2 = node2->as(); + const DsqlAliasNode* aliasNode1 = nodeAs(node1); + const DsqlAliasNode* aliasNode2 = nodeAs(node2); // We don't care about the alias itself but only about its field. if (aliasNode1 || aliasNode2) @@ -850,8 +850,8 @@ bool PASS1_node_match(const ExprNode* node1, const ExprNode* node2, bool ignoreM // Handle derived fields. - const DerivedFieldNode* derivedField1 = node1->as(); - const DerivedFieldNode* derivedField2 = node2->as(); + const DerivedFieldNode* derivedField1 = nodeAs(node1); + const DerivedFieldNode* derivedField2 = nodeAs(node2); if (derivedField1 || derivedField2) { @@ -998,7 +998,7 @@ RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* i dsqlScratch->aliasRelationPrefix = pass1_alias_concat(aliasRelationPrefix, alias); RecordSourceNode* query = input->querySpec; - UnionSourceNode* unionQuery = query->as(); + UnionSourceNode* unionQuery = nodeAs(query); RseNode* rse = NULL; const bool isRecursive = unionQuery && unionQuery->recursive; USHORT recursive_map_ctx = 0; @@ -1046,7 +1046,7 @@ RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* i // the worse thing is that a UNION currently can't be used in // deciding the JOIN order. bool foundSubSelect = false; - RseNode* queryNode = query->as(); + RseNode* queryNode = nodeAs(query); if (queryNode) foundSubSelect = SubSelectFinder::find(queryNode->dsqlSelectList); @@ -1146,7 +1146,7 @@ RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* i rse->dsqlSelectList->items[count]); // Auto-create dummy column name for pass1_any() - if (ignoreColumnChecks && !ExprNode::is(select_item)) + if (ignoreColumnChecks && !nodeIs(select_item)) { MAKE_desc(dsqlScratch, &select_item->nodDesc, select_item); @@ -1174,7 +1174,7 @@ RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* i ValueExprNode* select_item = rse->dsqlSelectList->items[count]; DerivedFieldNode* derivedField; - if ((derivedField = ExprNode::as(select_item))) + if ((derivedField = nodeAs(select_item))) derivedField->context = context; else { @@ -1190,13 +1190,11 @@ RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* i // Check for ambiguous column names inside this derived table. for (count = 0; count < rse->dsqlSelectList->items.getCount(); ++count) { - const DerivedFieldNode* selectItem1 = - rse->dsqlSelectList->items[count]->as(); + const DerivedFieldNode* selectItem1 = nodeAs(rse->dsqlSelectList->items[count]); for (FB_SIZE_T count2 = (count + 1); count2 < rse->dsqlSelectList->items.getCount(); ++count2) { - const DerivedFieldNode* selectItem2 = - rse->dsqlSelectList->items[count2]->as(); + const DerivedFieldNode* selectItem2 = nodeAs(rse->dsqlSelectList->items[count2]); if (selectItem1->name == selectItem2->name) { @@ -1252,10 +1250,10 @@ RseNode* PASS1_derived_table(DsqlCompilerScratch* dsqlScratch, SelectExprNode* i ValueExprNode* map_item = items->items[0]; DerivedFieldNode* derivedField; - if ((derivedField = ExprNode::as(map_item))) + if ((derivedField = nodeAs(map_item))) map_item = derivedField->value; - dsql_ctx* map_context = ExprNode::as(map_item)->context; + dsql_ctx* map_context = nodeAs(map_item)->context; map_context->ctx_flags |= CTX_recursive; map_context->ctx_recursive = recursive_map_ctx; @@ -1314,7 +1312,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, RelationSourceNode* relNode; FieldNode* fieldNode; - if ((rseNode = ExprNode::as(node))) + if ((rseNode = nodeAs(node))) { ValueListNode* sub_items = rseNode->dsqlSelectList; @@ -1331,7 +1329,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, DerivedFieldNode* derivedField; - if (!(derivedField = select_item->as())) + if (!(derivedField = nodeAs(select_item))) { // Internal dsql error: alias type expected by PASS1_expand_select_node ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << @@ -1358,7 +1356,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, } } } - else if ((procNode = ExprNode::as(node))) + else if ((procNode = nodeAs(node))) { dsql_ctx* context = procNode->dsqlContext; @@ -1378,7 +1376,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, } } } - else if ((relNode = ExprNode::as(node))) + else if ((relNode = nodeAs(node))) { dsql_ctx* context = relNode->dsqlContext; @@ -1398,7 +1396,7 @@ void PASS1_expand_select_node(DsqlCompilerScratch* dsqlScratch, ExprNode* node, } } } - else if ((fieldNode = ExprNode::as(node))) + else if ((fieldNode = nodeAs(node))) { RecordSourceNode* recSource = NULL; ValueExprNode* value = fieldNode->internalDsqlPass(dsqlScratch, &recSource); @@ -1455,7 +1453,7 @@ static ValueListNode* pass1_group_by_list(DsqlCompilerScratch* dsqlScratch, Valu FieldNode* field; LiteralNode* literal; - if ((field = ExprNode::as(sub))) + if ((field = nodeAs(sub))) { // check for alias or field node if (selectList && field->dsqlQualifier.isEmpty() && field->dsqlName.hasData()) @@ -1469,7 +1467,7 @@ static ValueListNode* pass1_group_by_list(DsqlCompilerScratch* dsqlScratch, Valu if (!frnode) frnode = field->internalDsqlPass(dsqlScratch, NULL); } - else if ((literal = ExprNode::as(sub)) && (literal->litDesc.dsc_dtype == dtype_long)) + else if ((literal = nodeAs(sub)) && (literal->litDesc.dsc_dtype == dtype_long)) { const ULONG position = literal->getSlong(); @@ -1532,17 +1530,17 @@ ValueExprNode* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const MetaNa FieldNode* fieldNode; DerivedFieldNode* derivedField; - if ((aliasNode = node->as())) + if ((aliasNode = nodeAs(node))) { if (aliasNode->name == name) matchingNode = node; } - else if ((fieldNode = node->as())) + else if ((fieldNode = nodeAs(node))) { if (fieldNode->dsqlField->fld_name == name.c_str()) matchingNode = node; } - else if ((derivedField = node->as())) + else if ((derivedField = nodeAs(node))) { if (derivedField->name == name) matchingNode = node; @@ -1559,11 +1557,11 @@ ValueExprNode* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const MetaNa TEXT buffer1[256]; buffer1[0] = 0; - if (returnNode->is()) + if (nodeIs(returnNode)) strcat(buffer1, "an alias"); - else if (returnNode->is()) + else if (nodeIs(returnNode)) strcat(buffer1, "a field"); - else if (returnNode->is()) + else if (nodeIs(returnNode)) strcat(buffer1, "a derived field"); else strcat(buffer1, "an item"); @@ -1571,11 +1569,11 @@ ValueExprNode* PASS1_lookup_alias(DsqlCompilerScratch* dsqlScratch, const MetaNa TEXT buffer2[256]; buffer2[0] = 0; - if (matchingNode->is()) + if (nodeIs(matchingNode)) strcat(buffer2, "an alias"); - else if (matchingNode->is()) + else if (nodeIs(matchingNode)) strcat(buffer2, "a field"); - else if (matchingNode->is()) + else if (nodeIs(matchingNode)) strcat(buffer2, "a derived field"); else strcat(buffer2, "an item"); @@ -1617,7 +1615,7 @@ static ValueExprNode* pass1_make_derived_field(thread_db* tdbb, DsqlCompilerScra FieldNode* fieldNode; DerivedFieldNode* derivedField; - if ((aliasNode = ExprNode::as(select_item))) + if ((aliasNode = nodeAs(select_item))) { // Create a derived field and ignore alias node. DerivedFieldNode* newField = FB_NEW_POOL(pool) DerivedFieldNode(pool, @@ -1625,25 +1623,25 @@ static ValueExprNode* pass1_make_derived_field(thread_db* tdbb, DsqlCompilerScra newField->nodDesc = aliasNode->value->nodDesc; return newField; } - else if ((subQueryNode = ExprNode::as(select_item))) + else if ((subQueryNode = nodeAs(select_item))) { // Try to generate derived field from sub-select ValueExprNode* derived_field = pass1_make_derived_field(tdbb, dsqlScratch, subQueryNode->value1); - if ((derivedField = ExprNode::as(derived_field))) + if ((derivedField = nodeAs(derived_field))) { derivedField->value = select_item; return derived_field; } } - else if ((mapNode = ExprNode::as(select_item))) + else if ((mapNode = nodeAs(select_item))) { // Aggregate's have map on top. ValueExprNode* derived_field = pass1_make_derived_field(tdbb, dsqlScratch, mapNode->map->map_node); // If we had succesfully made a derived field node change it with orginal map. - if ((derivedField = ExprNode::as(derived_field))) + if ((derivedField = nodeAs(derived_field))) { derivedField->value = select_item; derivedField->scope = dsqlScratch->scopeLevel; @@ -1651,7 +1649,7 @@ static ValueExprNode* pass1_make_derived_field(thread_db* tdbb, DsqlCompilerScra return derived_field; } } - else if ((fieldNode = ExprNode::as(select_item))) + else if ((fieldNode = nodeAs(select_item))) { // Create a derived field and hook in. @@ -1660,7 +1658,7 @@ static ValueExprNode* pass1_make_derived_field(thread_db* tdbb, DsqlCompilerScra newField->nodDesc = fieldNode->nodDesc; return newField; } - else if ((derivedField = ExprNode::as(select_item))) + else if ((derivedField = nodeAs(select_item))) { // Create a derived field that points to a derived field. @@ -1729,7 +1727,7 @@ static RseNode* pass1_rse(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* in ValueListNode* order, RowsClause* rows, bool updateLock, USHORT flags) { string save_alias; - RseNode* rseNode = input->as(); + RseNode* rseNode = nodeAs(input); const bool isRecursive = rseNode && (rseNode->dsqlFlags & RecordSourceNode::DFLAG_RECURSIVE); AutoSetRestore autoScopeLevel(&dsqlScratch->scopeLevel, dsqlScratch->scopeLevel); @@ -1764,8 +1762,8 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod thread_db* tdbb = JRD_get_thread_data(); MemoryPool& pool = *tdbb->getDefaultPool(); - SelectExprNode* selNode = input->as(); - UnionSourceNode* unionNode = input->as(); + SelectExprNode* selNode = nodeAs(input); + UnionSourceNode* unionNode = nodeAs(input); if (selNode) { @@ -1799,7 +1797,7 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod return pass1_union(dsqlScratch, unionNode, order, rows, updateLock, flags); } - RseNode* inputRse = input->as(); + RseNode* inputRse = nodeAs(input); fb_assert(inputRse); // Save the original base of the context stack and process relations @@ -1819,7 +1817,7 @@ static RseNode* pass1_rse_impl(DsqlCompilerScratch* dsqlScratch, RecordSourceNod if (updateLock && (streamList->items.getCount() != 1 || - !(relNode = streamList->items[0]->as()) || + !(relNode = nodeAs(streamList->items[0])) || !(relation = relNode->dsqlContext->ctx_relation) || (relation->rel_flags & (REL_view | REL_external)))) { @@ -2308,7 +2306,7 @@ ValueListNode* PASS1_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* input for (FB_SIZE_T sortloop = 0; sortloop < input->items.getCount(); ++sortloop) { DEV_BLKCHK(input->items[sortloop], dsql_type_nod); - NestConst node1 = input->items[sortloop]->as(); + NestConst node1 = nodeAs(input->items[sortloop]); if (!node1) { ERRD_post(Arg::Gds(isc_sqlerr) << Arg::Num(-104) << @@ -2320,7 +2318,7 @@ ValueListNode* PASS1_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* input // get node of value to be ordered by NestConst orderValue = node1->value; - NestConst collateNode = orderValue->as(); + NestConst collateNode = nodeAs(orderValue); if (collateNode) { @@ -2331,7 +2329,7 @@ ValueListNode* PASS1_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* input FieldNode* field; LiteralNode* literal; - if ((field = orderValue->as())) + if ((field = nodeAs(orderValue))) { ValueExprNode* aliasNode = NULL; @@ -2346,7 +2344,7 @@ ValueListNode* PASS1_sort(DsqlCompilerScratch* dsqlScratch, ValueListNode* input orderValue = aliasNode ? aliasNode : field->internalDsqlPass(dsqlScratch, NULL); } - else if ((literal = orderValue->as()) && literal->litDesc.dsc_dtype == dtype_long) + else if ((literal = nodeAs(orderValue)) && literal->litDesc.dsc_dtype == dtype_long) { const ULONG position = literal->getSlong(); @@ -2444,7 +2442,7 @@ static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* i dsqlScratch->scopeLevel--; if (updateLock) - (*uptr)->as()->flags &= ~RseNode::FLAG_WRITELOCK; + nodeAs(*uptr)->flags &= ~RseNode::FLAG_WRITELOCK; while (*(dsqlScratch->context) != base) dsqlScratch->unionContext.push(dsqlScratch->context->pop()); @@ -2457,14 +2455,14 @@ static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* i } // end scope block // generate the list of fields to select. - ValueListNode* items = unionSource->dsqlClauses->items[0]->as()->dsqlSelectList; + ValueListNode* items = nodeAs(unionSource->dsqlClauses->items[0])->dsqlSelectList; // loop through the list nodes, checking to be sure that they have the // same number of items for (FB_SIZE_T i = 1; i < unionSource->dsqlClauses->items.getCount(); ++i) { - const ValueListNode* nod1 = unionSource->dsqlClauses->items[i]->as()->dsqlSelectList; + const ValueListNode* nod1 = nodeAs(unionSource->dsqlClauses->items[i])->dsqlSelectList; if (items->items.getCount() != nod1->items.getCount()) { @@ -2495,7 +2493,7 @@ static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* i { for (FB_SIZE_T i = 0; i < unionSource->dsqlClauses->items.getCount(); ++i) { - ValueListNode* nod1 = unionSource->dsqlClauses->items[i]->as()->dsqlSelectList; + ValueListNode* nod1 = nodeAs(unionSource->dsqlClauses->items[i])->dsqlSelectList; MAKE_desc(dsqlScratch, &nod1->items[j]->nodDesc, nod1->items[j]); tmp_list->items[i] = nod1->items[j]; @@ -2520,7 +2518,7 @@ static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* i pass1_union_auto_cast(dsqlScratch, unionSource->dsqlClauses, desc, j); } - items = unionSource->dsqlClauses->items[0]->as()->dsqlSelectList; + items = nodeAs(unionSource->dsqlClauses->items[0])->dsqlSelectList; // Create mappings for union. @@ -2559,14 +2557,14 @@ static RseNode* pass1_union(DsqlCompilerScratch* dsqlScratch, UnionSourceNode* i ptr != end; ++ptr, ++uptr) { - OrderNode* order1 = (*ptr)->as(); + OrderNode* order1 = nodeAs(*ptr); const ValueExprNode* position = order1->value; - const CollateNode* collateNode = position->as(); + const CollateNode* collateNode = nodeAs(position); if (collateNode) position = collateNode->arg; - const LiteralNode* literal = position->as(); + const LiteralNode* literal = nodeAs(position); if (!literal || literal->litDesc.dsc_dtype != dtype_long) { @@ -2645,26 +2643,26 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in RseNode* rseNode; UnionSourceNode* unionNode; - if ((recSourceList = input->as())) + if ((recSourceList = nodeAs(input))) { NestConst* ptr = recSourceList->items.begin(); for (const NestConst* const end = recSourceList->items.end(); ptr != end; ++ptr) pass1_union_auto_cast(dsqlScratch, *ptr, desc, position); } - else if ((rseNode = input->as()) && !rseNode->dsqlExplicitJoin && + else if ((rseNode = nodeAs(input)) && !rseNode->dsqlExplicitJoin && !rseNode->dsqlContext) // not derived table { pass1_union_auto_cast(dsqlScratch, rseNode->dsqlStreams, desc, position); if (rseNode->dsqlStreams->items.getCount() == 1 && - (unionNode = rseNode->dsqlStreams->items[0]->as()) && + (unionNode = nodeAs(rseNode->dsqlStreams->items[0])) && unionNode->dsqlParentRse == rseNode) { // We're now in a UNION under a UNION so don't change the existing mappings. // Only replace the node where the map points to, because they could be changed. ValueListNode* sub_rse_items = - unionNode->dsqlClauses->items[0]->as()->dsqlSelectList; - dsql_map* map = rseNode->dsqlSelectList->items[position]->as()->map; + nodeAs(unionNode->dsqlClauses->items[0])->dsqlSelectList; + dsql_map* map = nodeAs(rseNode->dsqlSelectList->items[position])->map; map->map_node = sub_rse_items->items[position]; rseNode->dsqlSelectList->items[position]->nodDesc = desc; } @@ -2697,15 +2695,15 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in DerivedFieldNode* derivedField; // Pick a existing cast if available else make a new one. - if ((aliasNode = ExprNode::as(select_item)) && - aliasNode->value && (castNode = aliasNode->value->as())) + if ((aliasNode = nodeAs(select_item)) && + aliasNode->value && (castNode = nodeAs(aliasNode->value))) { } - else if ((derivedField = ExprNode::as(select_item)) && - (castNode = derivedField->value->as())) + else if ((derivedField = nodeAs(select_item)) && + (castNode = nodeAs(derivedField->value))) { } - else if ((castNode = ExprNode::as(select_item))) + else if ((castNode = nodeAs(select_item))) { } else @@ -2719,9 +2717,9 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in // We want to leave the ALIAS node on his place, because a UNION // uses the select_items from the first sub-rse to determine the // columnname. - if ((aliasNode = ExprNode::as(select_item))) + if ((aliasNode = nodeAs(select_item))) castNode->source = aliasNode->value; - else if ((derivedField = ExprNode::as(select_item))) + else if ((derivedField = nodeAs(select_item))) castNode->source = derivedField->value; else castNode->source = select_item; @@ -2731,7 +2729,7 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in const ValueExprNode* name_node = select_item; const DsqlMapNode* mapNode; - while ((mapNode = ExprNode::as(name_node))) + while ((mapNode = nodeAs(name_node))) { // Skip all the DsqlMapNodes. name_node = mapNode->map->map_node; @@ -2739,7 +2737,7 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in const FieldNode* fieldNode; - if ((fieldNode = ExprNode::as(name_node))) + if ((fieldNode = nodeAs(name_node))) { // Create new node for alias and copy fieldname. newAliasNode = FB_NEW_POOL(*tdbb->getDefaultPool()) DsqlAliasNode( @@ -2776,13 +2774,13 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in if (select_item->nodDesc.dsc_flags & DSC_nullable) castNode->nodDesc.dsc_flags |= DSC_nullable; - if ((aliasNode = ExprNode::as(select_item))) + if ((aliasNode = nodeAs(select_item))) { aliasNode->value = castNode; aliasNode->value->nodDesc = desc; select_item->nodDesc = desc; } - else if ((derivedField = ExprNode::as(select_item))) + else if ((derivedField = nodeAs(select_item))) { derivedField->value = castNode; derivedField->value->nodDesc = desc; @@ -2805,7 +2803,7 @@ static void pass1_union_auto_cast(DsqlCompilerScratch* dsqlScratch, ExprNode* in } } } - else if ((unionNode = input->as())) + else if ((unionNode = nodeAs(input))) { recSourceList = unionNode->dsqlClauses; @@ -2884,28 +2882,28 @@ static void remap_streams_to_parent_context(ExprNode* input, dsql_ctx* parent_co RseNode* rseNode; UnionSourceNode* unionNode; - if ((listNode = input->as())) + if ((listNode = nodeAs(input))) { NestConst* ptr = listNode->items.begin(); for (const NestConst* const end = listNode->items.end(); ptr != end; ++ptr) remap_streams_to_parent_context(*ptr, parent_context); } - else if ((procNode = input->as())) + else if ((procNode = nodeAs(input))) { DEV_BLKCHK(procNode->dsqlContext, dsql_type_ctx); procNode->dsqlContext->ctx_parent = parent_context; } - else if ((relNode = input->as())) + else if ((relNode = nodeAs(input))) { DEV_BLKCHK(relNode->dsqlContext, dsql_type_ctx); relNode->dsqlContext->ctx_parent = parent_context; } - else if ((rseNode = input->as())) + else if ((rseNode = nodeAs(input))) remap_streams_to_parent_context(rseNode->dsqlStreams, parent_context); - else if ((unionNode = input->as())) + else if ((unionNode = nodeAs(input))) remap_streams_to_parent_context(unionNode->dsqlClauses, parent_context); else - fb_assert(input->as()); + fb_assert(nodeAs(input)); } diff --git a/src/dsql/sqlda_pub.h b/src/dsql/sqlda_pub.h index 1f6ecf4243..e2505e5ea9 100644 --- a/src/dsql/sqlda_pub.h +++ b/src/dsql/sqlda_pub.h @@ -78,6 +78,8 @@ typedef struct #define SQL_TYPE_TIME 560 #define SQL_TYPE_DATE 570 #define SQL_INT64 580 +#define SQL_DEC16 32760 +#define SQL_DEC34 32762 #define SQL_BOOLEAN 32764 #define SQL_NULL 32766 diff --git a/src/gpre/c_cxx.cpp b/src/gpre/c_cxx.cpp index 8c667cf645..292492ebec 100644 --- a/src/gpre/c_cxx.cpp +++ b/src/gpre/c_cxx.cpp @@ -1901,7 +1901,11 @@ static void gen_emodify( const act* action, int column) align(column); gen_name(s1, source, true); gen_name(s2, reference, true); - if (field->fld_dtype > dtype_cstring || (field->fld_sub_type == 1 && field->fld_length == 1)) + if (field->fld_dtype == dtype_varying && field->fld_sub_type == dsc_text_type_fixed) + { + fprintf(gpreGlob.out_file, "memcpy (&%s, &%s, %d);", s2, s1, field->fld_length + sizeof(USHORT)); + } + else if (field->fld_dtype > dtype_cstring || (field->fld_sub_type == dsc_text_type_fixed && field->fld_length == 1)) { fprintf(gpreGlob.out_file, "%s = %s;", s2, s1); } @@ -3717,6 +3721,10 @@ static void make_port(const gpre_port* port, int column) dtype = DCL_LONG; break; + case dtype_varying: + fprintf(gpreGlob.out_file, " struct { ISC_USHORT length; ISC_UCHAR data[%d]; } isc_%d;\t/* %s */", field->fld_length, reference->ref_ident, name); + continue; + case dtype_cstring: case dtype_text: dtype = "char "; diff --git a/src/gpre/cmp.cpp b/src/gpre/cmp.cpp index e10d2693aa..77953e4100 100644 --- a/src/gpre/cmp.cpp +++ b/src/gpre/cmp.cpp @@ -788,7 +788,7 @@ static void cmp_field( gpre_req* request, const gpre_fld* field, break; case dtype_varying: - if (!(field->fld_flags & FLD_charset) && field->fld_ttype) + if (!(field->fld_flags & FLD_charset) && field->fld_ttype >= dsc_text_type_metadata) { request->add_byte(blr_varying); request->add_word(field->fld_length); @@ -1731,17 +1731,20 @@ static gpre_port* make_port( gpre_req* request, ref* reference) CPR_bugcheck("missing prototype field for value"); if (temp->ref_value && (temp->ref_flags & REF_array_elem)) field = field->fld_array; - if ((field->fld_length & 7) == 0) + FLD_LENGTH len = field->fld_length; + if (field->fld_dtype == dtype_varying) + len += sizeof(USHORT); + if ((len & 7) == 0) { temp->ref_next = alignments[2]; alignments[2] = temp; } - else if ((field->fld_length & 3) == 0) + else if ((len & 3) == 0) { temp->ref_next = alignments[1]; alignments[1] = temp; } - else if ((field->fld_length & 1) == 0) + else if ((len & 1) == 0) { temp->ref_next = alignments[0]; alignments[0] = temp; @@ -1774,7 +1777,10 @@ static gpre_port* make_port( gpre_req* request, ref* reference) #ifdef GPRE_FORTRAN reference->ref_offset = port->por_length; #endif - port->por_length += field->fld_length; + FLD_LENGTH len = field->fld_length; + if (field->fld_dtype == dtype_varying) + len += sizeof(USHORT); + port->por_length += len; } return port; diff --git a/src/gpre/movg.cpp b/src/gpre/movg.cpp index 1b2c20cbda..1abe62e9e5 100644 --- a/src/gpre/movg.cpp +++ b/src/gpre/movg.cpp @@ -49,7 +49,7 @@ static void post_error(const Firebird::Arg::StatusVector&); void MOVG_move(const dsc* from, dsc* to) { - CVT_move(from, to, post_error); + CVT_move(from, to, 0, post_error); } diff --git a/src/gpre/std/gpre_meta.epp b/src/gpre/std/gpre_meta.epp index b345894492..bfe9e0092f 100644 --- a/src/gpre/std/gpre_meta.epp +++ b/src/gpre/std/gpre_meta.epp @@ -878,85 +878,94 @@ USHORT MET_get_dtype(USHORT blr_dtype, USHORT sub_type, USHORT char_length, USHO { USHORT dtype; - USHORT l = *length; + USHORT corr_length = *length; switch (blr_dtype) { case blr_varying: case blr_text: - if (char_length != 0 && sub_type != dsc_text_type_none) - l = char_length * 4; + if (char_length != 0 && sub_type >= dsc_text_type_metadata) // subtypes below are single byte characters + corr_length = char_length * 4; dtype = dtype_text; - if (gpreGlob.sw_cstring && sub_type != dsc_text_type_fixed) + if (sub_type == dsc_text_type_fixed) { - ++l; + if (blr_dtype == blr_varying) + { + dtype = dtype_varying; + // Round up size of field to get right alignment and workaround message length mismatch error + corr_length = (corr_length + 1) & ~1U; + } + } + else if (gpreGlob.sw_cstring) + { + ++corr_length; dtype = dtype_cstring; } break; case blr_cstring: dtype = dtype_cstring; - if (char_length != 0 && sub_type != dsc_text_type_none) - l = char_length * 4; - ++l; + if (char_length != 0 && sub_type >= dsc_text_type_metadata) // subtypes below are single byte characters + corr_length = char_length * 4; + ++corr_length; break; case blr_short: dtype = dtype_short; - l = sizeof(SSHORT); + corr_length = sizeof(SSHORT); break; case blr_long: dtype = dtype_long; - l = sizeof(SLONG); + corr_length = sizeof(SLONG); break; case blr_quad: dtype = dtype_quad; - l = sizeof(ISC_QUAD); + corr_length = sizeof(ISC_QUAD); break; case blr_float: dtype = dtype_real; - l = sizeof(float); + corr_length = sizeof(float); break; case blr_double: dtype = dtype_double; - l = sizeof(double); + corr_length = sizeof(double); break; case blr_blob: dtype = dtype_blob; - l = sizeof(ISC_QUAD); + corr_length = sizeof(ISC_QUAD); break; // Begin sql date/time/timestamp case blr_sql_date: dtype = dtype_sql_date; - l = sizeof(ISC_DATE); + corr_length = sizeof(ISC_DATE); break; case blr_sql_time: dtype = dtype_sql_time; - l = sizeof(ISC_TIME); + corr_length = sizeof(ISC_TIME); break; case blr_timestamp: dtype = dtype_timestamp; - l = sizeof(ISC_TIMESTAMP); + corr_length = sizeof(ISC_TIMESTAMP); break; // End sql date/time/timestamp case blr_int64: dtype = dtype_int64; - l = sizeof(ISC_INT64); + corr_length = sizeof(ISC_INT64); break; case blr_bool: dtype = dtype_boolean; - l = sizeof(UCHAR); + corr_length = sizeof(UCHAR); break; default: @@ -964,7 +973,7 @@ USHORT MET_get_dtype(USHORT blr_dtype, USHORT sub_type, USHORT char_length, USHO return 0; // silence non initialized warning } - *length = l; + *length = corr_length; return dtype; } diff --git a/src/include/firebird/FirebirdInterface.idl b/src/include/firebird/FirebirdInterface.idl index 9760a94b4b..5739224c5b 100644 --- a/src/include/firebird/FirebirdInterface.idl +++ b/src/include/firebird/FirebirdInterface.idl @@ -29,6 +29,8 @@ typedef ISC_DATE; typedef ISC_QUAD; typedef ISC_TIME; +typedef FB_DEC16; +typedef FB_DEC34; // Versioned interface - base for all FB interfaces interface Versioned @@ -983,8 +985,10 @@ interface Util : Versioned XpbBuilder getXpbBuilder(Status status, uint kind, const uchar* buf, uint len); uint setOffsets(Status status, MessageMetadata metadata, OffsetsCallback callback); -version: +version: // 3.0 => 4.0 EventBlock createEventBlock(Status status, const string* events); + DecFloat16 getDecFloat16(Status status); + DecFloat34 getDecFloat34(Status status); } interface OffsetsCallback : Versioned @@ -1366,3 +1370,23 @@ interface UdrPlugin : Versioned void registerProcedure(Status status, const string name, UdrProcedureFactory factory); void registerTrigger(Status status, const string name, UdrTriggerFactory factory); } + +interface DecFloat16 : Versioned +{ + const uint BCD_SIZE = 16; + const uint STRING_SIZE = 24; // includes terminating \0 + void toBcd(const FB_DEC16* from, int* sign, uchar* bcd, int* exp); + void toString(Status status, const FB_DEC16* from, uint bufferLength, string buffer); + void fromBcd(int sign, const uchar* bcd, int exp, FB_DEC16* to); + void fromString(Status status, const string from, FB_DEC16* to); +} + +interface DecFloat34 : Versioned +{ + const uint BCD_SIZE = 34; + const uint STRING_SIZE = 43; // includes terminating \0 + void toBcd(const FB_DEC34* from, int* sign, uchar* bcd, int* exp); + void toString(Status status, const FB_DEC34* from, uint bufferLength, string buffer); + void fromBcd(int sign, const uchar* bcd, int exp, FB_DEC34* to); + void fromString(Status status, const string from, FB_DEC34* to); +} diff --git a/src/include/firebird/IdlFbInterfaces.h b/src/include/firebird/IdlFbInterfaces.h index 4f9fa90c7b..423c01322f 100644 --- a/src/include/firebird/IdlFbInterfaces.h +++ b/src/include/firebird/IdlFbInterfaces.h @@ -111,6 +111,8 @@ namespace Firebird class IUdrProcedureFactory; class IUdrTriggerFactory; class IUdrPlugin; + class IDecFloat16; + class IDecFloat34; // Interfaces declarations @@ -3695,6 +3697,8 @@ namespace Firebird IXpbBuilder* (CLOOP_CARG *getXpbBuilder)(IUtil* self, IStatus* status, unsigned kind, const unsigned char* buf, unsigned len) throw(); unsigned (CLOOP_CARG *setOffsets)(IUtil* self, IStatus* status, IMessageMetadata* metadata, IOffsetsCallback* callback) throw(); IEventBlock* (CLOOP_CARG *createEventBlock)(IUtil* self, IStatus* status, const char** events) throw(); + IDecFloat16* (CLOOP_CARG *getDecFloat16)(IUtil* self, IStatus* status) throw(); + IDecFloat34* (CLOOP_CARG *getDecFloat34)(IUtil* self, IStatus* status) throw(); }; protected: @@ -3809,6 +3813,34 @@ namespace Firebird StatusType::checkException(status); return ret; } + + template IDecFloat16* getDecFloat16(StatusType* status) + { + if (cloopVTable->version < 3) + { + StatusType::setVersionError(status, "IUtil", cloopVTable->version, 3); + StatusType::checkException(status); + return 0; + } + StatusType::clearException(status); + IDecFloat16* ret = static_cast(this->cloopVTable)->getDecFloat16(this, status); + StatusType::checkException(status); + return ret; + } + + template IDecFloat34* getDecFloat34(StatusType* status) + { + if (cloopVTable->version < 3) + { + StatusType::setVersionError(status, "IUtil", cloopVTable->version, 3); + StatusType::checkException(status); + return 0; + } + StatusType::clearException(status); + IDecFloat34* ret = static_cast(this->cloopVTable)->getDecFloat34(this, status); + StatusType::checkException(status); + return ret; + } }; class IOffsetsCallback : public IVersioned @@ -5270,6 +5302,110 @@ namespace Firebird } }; + class IDecFloat16 : public IVersioned + { + public: + struct VTable : public IVersioned::VTable + { + void (CLOOP_CARG *toBcd)(IDecFloat16* self, const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp) throw(); + void (CLOOP_CARG *toString)(IDecFloat16* self, IStatus* status, const FB_DEC16* from, unsigned bufferLength, char* buffer) throw(); + void (CLOOP_CARG *fromBcd)(IDecFloat16* self, int sign, const unsigned char* bcd, int exp, FB_DEC16* to) throw(); + void (CLOOP_CARG *fromString)(IDecFloat16* self, IStatus* status, const char* from, FB_DEC16* to) throw(); + }; + + protected: + IDecFloat16(DoNotInherit) + : IVersioned(DoNotInherit()) + { + } + + ~IDecFloat16() + { + } + + public: + static const unsigned VERSION = 2; + + static const unsigned BCD_SIZE = 16; + static const unsigned STRING_SIZE = 24; + + void toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp) + { + static_cast(this->cloopVTable)->toBcd(this, from, sign, bcd, exp); + } + + template void toString(StatusType* status, const FB_DEC16* from, unsigned bufferLength, char* buffer) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->toString(this, status, from, bufferLength, buffer); + StatusType::checkException(status); + } + + void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC16* to) + { + static_cast(this->cloopVTable)->fromBcd(this, sign, bcd, exp, to); + } + + template void fromString(StatusType* status, const char* from, FB_DEC16* to) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->fromString(this, status, from, to); + StatusType::checkException(status); + } + }; + + class IDecFloat34 : public IVersioned + { + public: + struct VTable : public IVersioned::VTable + { + void (CLOOP_CARG *toBcd)(IDecFloat34* self, const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp) throw(); + void (CLOOP_CARG *toString)(IDecFloat34* self, IStatus* status, const FB_DEC34* from, unsigned bufferLength, char* buffer) throw(); + void (CLOOP_CARG *fromBcd)(IDecFloat34* self, int sign, const unsigned char* bcd, int exp, FB_DEC34* to) throw(); + void (CLOOP_CARG *fromString)(IDecFloat34* self, IStatus* status, const char* from, FB_DEC34* to) throw(); + }; + + protected: + IDecFloat34(DoNotInherit) + : IVersioned(DoNotInherit()) + { + } + + ~IDecFloat34() + { + } + + public: + static const unsigned VERSION = 2; + + static const unsigned BCD_SIZE = 34; + static const unsigned STRING_SIZE = 43; + + void toBcd(const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp) + { + static_cast(this->cloopVTable)->toBcd(this, from, sign, bcd, exp); + } + + template void toString(StatusType* status, const FB_DEC34* from, unsigned bufferLength, char* buffer) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->toString(this, status, from, bufferLength, buffer); + StatusType::checkException(status); + } + + void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC34* to) + { + static_cast(this->cloopVTable)->fromBcd(this, sign, bcd, exp, to); + } + + template void fromString(StatusType* status, const char* from, FB_DEC34* to) + { + StatusType::clearException(status); + static_cast(this->cloopVTable)->fromString(this, status, from, to); + StatusType::checkException(status); + } + }; + // Interfaces implementations template @@ -12941,6 +13077,8 @@ namespace Firebird this->getXpbBuilder = &Name::cloopgetXpbBuilderDispatcher; this->setOffsets = &Name::cloopsetOffsetsDispatcher; this->createEventBlock = &Name::cloopcreateEventBlockDispatcher; + this->getDecFloat16 = &Name::cloopgetDecFloat16Dispatcher; + this->getDecFloat34 = &Name::cloopgetDecFloat34Dispatcher; } } vTable; @@ -13138,6 +13276,36 @@ namespace Firebird return static_cast(0); } } + + static IDecFloat16* CLOOP_CARG cloopgetDecFloat16Dispatcher(IUtil* self, IStatus* status) throw() + { + StatusType status2(status); + + try + { + return static_cast(self)->Name::getDecFloat16(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + return static_cast(0); + } + } + + static IDecFloat34* CLOOP_CARG cloopgetDecFloat34Dispatcher(IUtil* self, IStatus* status) throw() + { + StatusType status2(status); + + try + { + return static_cast(self)->Name::getDecFloat34(&status2); + } + catch (...) + { + StatusType::catchException(&status2); + return static_cast(0); + } + } }; template > > @@ -13167,6 +13335,8 @@ namespace Firebird virtual IXpbBuilder* getXpbBuilder(StatusType* status, unsigned kind, const unsigned char* buf, unsigned len) = 0; virtual unsigned setOffsets(StatusType* status, IMessageMetadata* metadata, IOffsetsCallback* callback) = 0; virtual IEventBlock* createEventBlock(StatusType* status, const char** events) = 0; + virtual IDecFloat16* getDecFloat16(StatusType* status) = 0; + virtual IDecFloat34* getDecFloat34(StatusType* status) = 0; }; template @@ -16357,6 +16527,196 @@ namespace Firebird virtual void registerProcedure(StatusType* status, const char* name, IUdrProcedureFactory* factory) = 0; virtual void registerTrigger(StatusType* status, const char* name, IUdrTriggerFactory* factory) = 0; }; + + template + class IDecFloat16BaseImpl : public Base + { + public: + typedef IDecFloat16 Declaration; + + IDecFloat16BaseImpl(DoNotInherit = DoNotInherit()) + { + static struct VTableImpl : Base::VTable + { + VTableImpl() + { + this->version = Base::VERSION; + this->toBcd = &Name::clooptoBcdDispatcher; + this->toString = &Name::clooptoStringDispatcher; + this->fromBcd = &Name::cloopfromBcdDispatcher; + this->fromString = &Name::cloopfromStringDispatcher; + } + } vTable; + + this->cloopVTable = &vTable; + } + + static void CLOOP_CARG clooptoBcdDispatcher(IDecFloat16* self, const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp) throw() + { + try + { + static_cast(self)->Name::toBcd(from, sign, bcd, exp); + } + catch (...) + { + StatusType::catchException(0); + } + } + + static void CLOOP_CARG clooptoStringDispatcher(IDecFloat16* self, IStatus* status, const FB_DEC16* from, unsigned bufferLength, char* buffer) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::toString(&status2, from, bufferLength, buffer); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + + static void CLOOP_CARG cloopfromBcdDispatcher(IDecFloat16* self, int sign, const unsigned char* bcd, int exp, FB_DEC16* to) throw() + { + try + { + static_cast(self)->Name::fromBcd(sign, bcd, exp, to); + } + catch (...) + { + StatusType::catchException(0); + } + } + + static void CLOOP_CARG cloopfromStringDispatcher(IDecFloat16* self, IStatus* status, const char* from, FB_DEC16* to) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::fromString(&status2, from, to); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + }; + + template > > + class IDecFloat16Impl : public IDecFloat16BaseImpl + { + protected: + IDecFloat16Impl(DoNotInherit = DoNotInherit()) + { + } + + public: + virtual ~IDecFloat16Impl() + { + } + + virtual void toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp) = 0; + virtual void toString(StatusType* status, const FB_DEC16* from, unsigned bufferLength, char* buffer) = 0; + virtual void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC16* to) = 0; + virtual void fromString(StatusType* status, const char* from, FB_DEC16* to) = 0; + }; + + template + class IDecFloat34BaseImpl : public Base + { + public: + typedef IDecFloat34 Declaration; + + IDecFloat34BaseImpl(DoNotInherit = DoNotInherit()) + { + static struct VTableImpl : Base::VTable + { + VTableImpl() + { + this->version = Base::VERSION; + this->toBcd = &Name::clooptoBcdDispatcher; + this->toString = &Name::clooptoStringDispatcher; + this->fromBcd = &Name::cloopfromBcdDispatcher; + this->fromString = &Name::cloopfromStringDispatcher; + } + } vTable; + + this->cloopVTable = &vTable; + } + + static void CLOOP_CARG clooptoBcdDispatcher(IDecFloat34* self, const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp) throw() + { + try + { + static_cast(self)->Name::toBcd(from, sign, bcd, exp); + } + catch (...) + { + StatusType::catchException(0); + } + } + + static void CLOOP_CARG clooptoStringDispatcher(IDecFloat34* self, IStatus* status, const FB_DEC34* from, unsigned bufferLength, char* buffer) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::toString(&status2, from, bufferLength, buffer); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + + static void CLOOP_CARG cloopfromBcdDispatcher(IDecFloat34* self, int sign, const unsigned char* bcd, int exp, FB_DEC34* to) throw() + { + try + { + static_cast(self)->Name::fromBcd(sign, bcd, exp, to); + } + catch (...) + { + StatusType::catchException(0); + } + } + + static void CLOOP_CARG cloopfromStringDispatcher(IDecFloat34* self, IStatus* status, const char* from, FB_DEC34* to) throw() + { + StatusType status2(status); + + try + { + static_cast(self)->Name::fromString(&status2, from, to); + } + catch (...) + { + StatusType::catchException(&status2); + } + } + }; + + template > > + class IDecFloat34Impl : public IDecFloat34BaseImpl + { + protected: + IDecFloat34Impl(DoNotInherit = DoNotInherit()) + { + } + + public: + virtual ~IDecFloat34Impl() + { + } + + virtual void toBcd(const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp) = 0; + virtual void toString(StatusType* status, const FB_DEC34* from, unsigned bufferLength, char* buffer) = 0; + virtual void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC34* to) = 0; + virtual void fromString(StatusType* status, const char* from, FB_DEC34* to) = 0; + }; }; diff --git a/src/include/firebird/Message.h b/src/include/firebird/Message.h index 7d529d7ce9..fcb8a56916 100644 --- a/src/include/firebird/Message.h +++ b/src/include/firebird/Message.h @@ -133,6 +133,14 @@ builder->setType(status, index, SQL_DOUBLE); \ builder->setLength(status, index, sizeof(double)); +#define FB__META_FB_DECFLOAT16 \ + builder->setType(status, index, SQL_DEC16); \ + builder->setLength(status, index, sizeof(FB_DEC16)); + +#define FB__META_FB_DECFLOAT34 \ + builder->setType(status, index, SQL_DEC34); \ + builder->setLength(status, index, sizeof(FB_DEC34)); + #define FB__META_FB_BLOB \ builder->setType(status, index, SQL_BLOB); \ builder->setLength(status, index, sizeof(ISC_QUAD)); @@ -185,6 +193,8 @@ #define FB__TYPE_FB_BIGINT ISC_INT64 #define FB__TYPE_FB_FLOAT float #define FB__TYPE_FB_DOUBLE double +#define FB__TYPE_FB_DECFLOAT16 FB_DEC16 +#define FB__TYPE_FB_DECFLOAT34 FB_DEC34 #define FB__TYPE_FB_BLOB ISC_QUAD #define FB__TYPE_FB_BOOLEAN ISC_UCHAR #define FB__TYPE_FB_DATE ::Firebird::FbDate diff --git a/src/include/firebird/UdrCppEngine.h b/src/include/firebird/UdrCppEngine.h index ce949bdcf0..26ef5a0e90 100644 --- a/src/include/firebird/UdrCppEngine.h +++ b/src/include/firebird/UdrCppEngine.h @@ -330,13 +330,13 @@ public: { CheckStatusWrapper statusWrapper(status); - if (!run(&statusWrapper, plugin, &IUdrPlugin::registerFunction, regFunctions)) + if (!run(&statusWrapper, plugin, &IUdrPlugin::registerFunction, regFunctions)) return; - if (!run(&statusWrapper, plugin, &IUdrPlugin::registerProcedure, regProcedures)) + if (!run(&statusWrapper, plugin, &IUdrPlugin::registerProcedure, regProcedures)) return; - if (!run(&statusWrapper, plugin, &IUdrPlugin::registerTrigger, regTriggers)) + if (!run(&statusWrapper, plugin, &IUdrPlugin::registerTrigger, regTriggers)) return; } diff --git a/src/include/gen/autoconfig_msvc.h b/src/include/gen/autoconfig_msvc.h index 81ef61a6b7..f750cf18b4 100644 --- a/src/include/gen/autoconfig_msvc.h +++ b/src/include/gen/autoconfig_msvc.h @@ -207,6 +207,11 @@ #define isnan _isnan #endif +#if !defined(HAS_NOEXCEPT) +#if defined(_MSC_FULL_VER) && _MSC_FULL_VER >= 190023026 +#define HAS_NOEXCEPT +#endif +#endif /* Types */ #undef HAVE_SOCKLEN_T diff --git a/src/include/gen/codetext.h b/src/include/gen/codetext.h index 9b314540e1..6d1069a761 100644 --- a/src/include/gen/codetext.h +++ b/src/include/gen/codetext.h @@ -839,6 +839,12 @@ static const struct { {"overriding_system_invalid", 335545135}, {"overriding_user_invalid", 335545136}, {"overriding_system_missing", 335545137}, + {"decprecision_err", 335545138}, + {"decfloat_divide_by_zero", 335545139}, + {"decfloat_inexact_result", 335545140}, + {"decfloat_invalid_operation", 335545141}, + {"decfloat_overflow", 335545142}, + {"decfloat_underflow", 335545143}, {"gfix_db_name", 335740929}, {"gfix_invalid_sw", 335740930}, {"gfix_incmp_sw", 335740932}, diff --git a/src/include/gen/iberror.h b/src/include/gen/iberror.h index 36794b0a8d..142dd0db2a 100644 --- a/src/include/gen/iberror.h +++ b/src/include/gen/iberror.h @@ -873,6 +873,12 @@ const ISC_STATUS isc_overriding_without_identity = 335545134L; const ISC_STATUS isc_overriding_system_invalid = 335545135L; const ISC_STATUS isc_overriding_user_invalid = 335545136L; const ISC_STATUS isc_overriding_system_missing = 335545137L; +const ISC_STATUS isc_decprecision_err = 335545138L; +const ISC_STATUS isc_decfloat_divide_by_zero = 335545139L; +const ISC_STATUS isc_decfloat_inexact_result = 335545140L; +const ISC_STATUS isc_decfloat_invalid_operation = 335545141L; +const ISC_STATUS isc_decfloat_overflow = 335545142L; +const ISC_STATUS isc_decfloat_underflow = 335545143L; const ISC_STATUS isc_gfix_db_name = 335740929L; const ISC_STATUS isc_gfix_invalid_sw = 335740930L; const ISC_STATUS isc_gfix_incmp_sw = 335740932L; @@ -1347,7 +1353,7 @@ const ISC_STATUS isc_trace_switch_user_only = 337182757L; const ISC_STATUS isc_trace_switch_param_miss = 337182758L; const ISC_STATUS isc_trace_param_act_notcompat = 337182759L; const ISC_STATUS isc_trace_mandatory_switch_miss = 337182760L; -const ISC_STATUS isc_err_max = 1291; +const ISC_STATUS isc_err_max = 1297; #else /* c definitions */ @@ -2190,6 +2196,12 @@ const ISC_STATUS isc_err_max = 1291; #define isc_overriding_system_invalid 335545135L #define isc_overriding_user_invalid 335545136L #define isc_overriding_system_missing 335545137L +#define isc_decprecision_err 335545138L +#define isc_decfloat_divide_by_zero 335545139L +#define isc_decfloat_inexact_result 335545140L +#define isc_decfloat_invalid_operation 335545141L +#define isc_decfloat_overflow 335545142L +#define isc_decfloat_underflow 335545143L #define isc_gfix_db_name 335740929L #define isc_gfix_invalid_sw 335740930L #define isc_gfix_incmp_sw 335740932L @@ -2664,7 +2676,7 @@ const ISC_STATUS isc_err_max = 1291; #define isc_trace_switch_param_miss 337182758L #define isc_trace_param_act_notcompat 337182759L #define isc_trace_mandatory_switch_miss 337182760L -#define isc_err_max 1291 +#define isc_err_max 1297 #endif diff --git a/src/include/gen/msgs.h b/src/include/gen/msgs.h index 6da69e955b..6fe2b426d3 100644 --- a/src/include/gen/msgs.h +++ b/src/include/gen/msgs.h @@ -474,7 +474,7 @@ static const struct { {335544770, "Array bounds exceeded. The code attempted to access an array element that is out of bounds."}, /* exception_array_bounds_exceeded */ {335544771, "Float denormal operand. One of the floating-point operands is too small to represent a standard float value."}, /* exception_float_denormal_operand */ {335544772, "Floating-point divide by zero. The code attempted to divide a floating-point value by zero."}, /* exception_float_divide_by_zero */ - {335544773, "Floating-point inexact result. The result of a floating-point operation cannot be represented as a deciaml fraction."}, /* exception_float_inexact_result */ + {335544773, "Floating-point inexact result. The result of a floating-point operation cannot be represented as a decimal fraction."}, /* exception_float_inexact_result */ {335544774, "Floating-point invalid operand. An indeterminant error occurred during a floating-point operation."}, /* exception_float_invalid_operand */ {335544775, "Floating-point overflow. The exponent of a floating-point operation is greater than the magnitude allowed."}, /* exception_float_overflow */ {335544776, "Floating-point stack check. The stack overflowed or underflowed as the result of a floating-point operation."}, /* exception_float_stack_check */ @@ -842,6 +842,12 @@ Data source : @4"}, /* eds_statement */ {335545135, "OVERRIDING SYSTEM VALUE can be used only for identity column defined as 'GENERATED ALWAYS' in INSERT for table/view @1"}, /* overriding_system_invalid */ {335545136, "OVERRIDING USER VALUE can be used only for identity column defined as 'GENERATED BY DEFAULT' in INSERT for table/view @1"}, /* overriding_user_invalid */ {335545137, "OVERRIDING SYSTEM VALUE should be used to override the value of an identity column defined as 'GENERATED ALWAYS' in table/view @1"}, /* overriding_system_missing */ + {335545138, "DecFloat precision must be 16 or 34"}, /* decprecision_err */ + {335545139, "Decimal float divide by zero. The code attempted to divide a DECFLOAT value by zero."}, /* decfloat_divide_by_zero */ + {335545140, "Decimal float inexact result. The result of an operation cannot be represented as a decimal fraction."}, /* decfloat_inexact_result */ + {335545141, "Decimal float invalid operation. An indeterminant error occurred during an operation."}, /* decfloat_invalid_operation */ + {335545142, "Decimal float overflow. The exponent of a result is greater than the magnitude allowed."}, /* decfloat_overflow */ + {335545143, "Decimal float underflow. The exponent of a result is less than the magnitude allowed."}, /* decfloat_underflow */ {335740929, "data base file name (@1) already given"}, /* gfix_db_name */ {335740930, "invalid switch @1"}, /* gfix_invalid_sw */ {335740932, "incompatible switch combination"}, /* gfix_incmp_sw */ diff --git a/src/include/gen/sql_code.h b/src/include/gen/sql_code.h index 092c9bbd6d..fd926c2db2 100644 --- a/src/include/gen/sql_code.h +++ b/src/include/gen/sql_code.h @@ -838,6 +838,12 @@ static const struct { {335545135, -902}, /* 815 overriding_system_invalid */ {335545136, -902}, /* 816 overriding_user_invalid */ {335545137, -902}, /* 817 overriding_system_missing */ + {335545138, -842}, /* 818 decprecision_err */ + {335545139, -901}, /* 819 decfloat_divide_by_zero */ + {335545140, -901}, /* 820 decfloat_inexact_result */ + {335545141, -901}, /* 821 decfloat_invalid_operation */ + {335545142, -901}, /* 822 decfloat_overflow */ + {335545143, -901}, /* 823 decfloat_underflow */ {335740929, -901}, /* 1 gfix_db_name */ {335740930, -901}, /* 2 gfix_invalid_sw */ {335740932, -901}, /* 4 gfix_incmp_sw */ diff --git a/src/include/gen/sql_state.h b/src/include/gen/sql_state.h index 06be5f599a..17bd675509 100644 --- a/src/include/gen/sql_state.h +++ b/src/include/gen/sql_state.h @@ -838,6 +838,12 @@ static const struct { {335545135, "42000"}, // 815 overriding_system_invalid {335545136, "42000"}, // 816 overriding_user_invalid {335545137, "42000"}, // 817 overriding_system_missing + {335545138, "HY104"}, // 818 decprecision_err + {335545139, "22012"}, // 819 decfloat_divide_by_zero + {335545140, "22000"}, // 820 decfloat_inexact_result + {335545141, "22000"}, // 821 decfloat_invalid_operation + {335545142, "22003"}, // 822 decfloat_overflow + {335545143, "22003"}, // 823 decfloat_underflow {335740929, "00000"}, // 1 gfix_db_name {335740930, "00000"}, // 2 gfix_invalid_sw {335740932, "00000"}, // 4 gfix_incmp_sw diff --git a/src/include/types_pub.h b/src/include/types_pub.h index 89c4ca6472..a3adda32e6 100644 --- a/src/include/types_pub.h +++ b/src/include/types_pub.h @@ -170,4 +170,15 @@ typedef struct GDS_QUAD_t ISC_QUAD; typedef int (*FB_SHUTDOWN_CALLBACK)(const int reason, const int mask, void* arg); +struct FB_DEC16_t { + ISC_UINT64 fb_data[1]; +}; + +struct FB_DEC34_t { + ISC_UINT64 fb_data[2]; +}; + +typedef struct FB_DEC16_t FB_DEC16; +typedef struct FB_DEC34_t FB_DEC34; + #endif /* INCLUDE_TYPES_PUB_H */ diff --git a/src/intl/lc_ascii.cpp b/src/intl/lc_ascii.cpp index 89667d9618..22cc9b9d9d 100644 --- a/src/intl/lc_ascii.cpp +++ b/src/intl/lc_ascii.cpp @@ -64,8 +64,7 @@ static void famasc_destroy(texttype* obj) if (impl) { - if (impl->cs.charset_fn_destroy) - impl->cs.charset_fn_destroy(&impl->cs); + Firebird::IntlUtil::finiCharset(&impl->cs); delete impl->charSet; delete impl; diff --git a/src/intl/lc_icu.cpp b/src/intl/lc_icu.cpp index cf547e5f69..a2d748b732 100644 --- a/src/intl/lc_icu.cpp +++ b/src/intl/lc_icu.cpp @@ -57,8 +57,7 @@ static bool texttype_default_init(texttype* tt, // test if that ICU charset exist if (CSICU_charset_init(&cs, charSetName)) { - if (cs.charset_fn_destroy) - cs.charset_fn_destroy(&cs); + IntlUtil::finiCharset(&cs); } else return false; diff --git a/src/intl/ld.cpp b/src/intl/ld.cpp index 202d70ecec..cf2e90fbcd 100644 --- a/src/intl/ld.cpp +++ b/src/intl/ld.cpp @@ -539,8 +539,7 @@ INTL_BOOL FB_DLL_EXPORT LD_lookup_texttype(texttype* tt, const ASCII* texttype_n INTL_BOOL ret = collations[i].ptr(tt, &cs, texttype_name, charset_name, attributes, specific_attributes, specific_attributes_length, config_info); - if (cs.charset_fn_destroy) - cs.charset_fn_destroy(&cs); + IntlUtil::finiCharset(&cs); return ret; } diff --git a/src/isql/isql.epp b/src/isql/isql.epp index fb08616d68..0dc1f6bfaa 100644 --- a/src/isql/isql.epp +++ b/src/isql/isql.epp @@ -309,6 +309,16 @@ static inline int fb_isdigit(const char c) } +IsqlGlobals::IsqlGlobals() +{ + Firebird::AutoPtr > + status_vector(fbMaster->getStatus()); + Firebird::CheckStatusWrapper statusWrapper(status_vector); + + df16 = Firebird::UtilInterfacePtr()->getDecFloat16(&statusWrapper); + df34 = Firebird::UtilInterfacePtr()->getDecFloat34(&statusWrapper); +} + // I s q l G l o b a l s : : p r i n t f // Output to the Out stream. void IsqlGlobals::printf(const char* buffer, ...) @@ -1825,11 +1835,14 @@ bool ISQL_printNumericType(const char* fieldName, const int fieldType, const int case SMALLINT: case INTEGER: case BIGINT: + case DOUBLE_PRECISION: // Handle Integral subtypes NUMERIC and DECIMAL // We are ODS >= 10 and could be any Dialect // We are Dialect >=3 since FIELD_PRECISION is non-NULL - if (isqlGlob.major_ods >= ODS_VERSION10 && fieldPrecision != 0 && + if (isqlGlob.major_ods >= ODS_VERSION10 && + fieldPrecision != 0 && + fieldType != DOUBLE_PRECISION && fieldSubType > 0 && fieldSubType <= MAX_INTSUBTYPES) { isqlGlob.printf("%s(%d, %d)", @@ -2223,6 +2236,8 @@ static processing_state add_row(TEXT* tabname) SINT64 n; float* fvalue; double* dvalue; + FB_DEC16* d64value; + FB_DEC34* d128value; UCHAR* boolean; ISC_QUAD* blobid; vary* avary; @@ -2289,6 +2304,28 @@ static processing_state add_row(TEXT* tabname) } break; + case SQL_DEC16: + d64value = (FB_DEC16*) datap; + if (isqlGlob.df16) + isqlGlob.df16->fromString(fbStatus, lastInputLine, d64value); + if ((!isqlGlob.df16) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) + { + STDERROUT("Input parsing problem"); + done = true; + } + break; + + case SQL_DEC34: + d128value = (FB_DEC34*) datap; + if (isqlGlob.df34) + isqlGlob.df34->fromString(fbStatus, lastInputLine, d128value); + if ((!isqlGlob.df34) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) + { + STDERROUT("Input parsing problem"); + done = true; + } + break; + case SQL_TYPE_DATE: if (3 != sscanf(lastInputLine, "%d/%d/%d", ×.tm_year, ×.tm_mon, ×.tm_mday) || @@ -2978,6 +3015,8 @@ static processing_state bulk_insert_hack(const char* command) vary* avary; char* achar; tm times; + FB_DEC16* d64value; + FB_DEC34* d128value; // Initialize the time structure. memset(×, 0, sizeof(times)); char msec_str[5] = ""; @@ -3037,6 +3076,28 @@ static processing_state bulk_insert_hack(const char* command) } break; + case SQL_DEC16: + d64value = (FB_DEC16*) datap; + if (isqlGlob.df16) + isqlGlob.df16->fromString(fbStatus, lastInputLine, d64value); + if ((!isqlGlob.df16) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) + { + STDERROUT("Input parsing problem"); + done = true; + } + break; + + case SQL_DEC34: + d128value = (FB_DEC34*) datap; + if (isqlGlob.df34) + isqlGlob.df34->fromString(fbStatus, lastInputLine, d128value); + if ((!isqlGlob.df34) || (fbStatus->getState() & Firebird::IStatus::STATE_ERRORS)) + { + STDERROUT("Input parsing problem"); + done = true; + } + break; + case SQL_TYPE_DATE: if (3 != sscanf(lastPos, "%d-%d-%d", ×.tm_year, ×.tm_mon, ×.tm_mday) || @@ -4807,6 +4868,7 @@ static processing_state frontend_set(const char* cmd, const char* const* parms, {SetOptions::sqlCont, "ROLE", 0}, {SetOptions::sqlCont, "TRUSTED", 0}, // TRUSTED ROLE, will get DSQL error other case {SetOptions::stmtTimeout, "LOCAL_TIMEOUT", 0}, + {SetOptions::sqlCont, "DECFLOAT", 0}, }; // Display current set options @@ -5433,7 +5495,7 @@ void ISQL_get_version(bool call_by_create_db) isc_info_ods_minor_version, isc_info_db_sql_dialect, frb_info_att_charset, - Version_info ? isc_info_firebird_version: isc_info_end, + UCHAR(Version_info ? isc_info_firebird_version: isc_info_end), isc_info_end }; /* @@ -6945,8 +7007,8 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) * Print an SQL data item as described. * **************************************/ + const char* convErr = "Conversion error"; TEXT d[32]; - TEXT* p = *s; *p = '\0'; @@ -7173,6 +7235,44 @@ static unsigned print_item(TEXT** s, const IsqlVar* var, const unsigned length) } break; + case SQL_DEC16: + { + char decStr[Firebird::IDecFloat16::STRING_SIZE]; + if (isqlGlob.df16) + { + isqlGlob.df16->toString(fbStatus, var->value.asDec16, sizeof(decStr), decStr); + if (ISQL_errmsg(fbStatus)) + strcpy(decStr, convErr); + } + else + strcpy(decStr, convErr); + + if (setValues.List) + isqlGlob.printf("%*.*s%s", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr, NEWLINE); + else + sprintf(p, "%*.*s ", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr); + } + break; + + case SQL_DEC34: + { + char decStr[Firebird::IDecFloat34::STRING_SIZE]; + if (isqlGlob.df34) + { + isqlGlob.df34->toString(fbStatus, var->value.asDec34, sizeof(decStr), decStr); + if (ISQL_errmsg(fbStatus)) + strcpy(decStr, convErr); + } + else + strcpy(decStr, convErr); + + if (setValues.List) + isqlGlob.printf("%*.*s%s", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr, NEWLINE); + else + sprintf(p, "%*.*s ", sizeof(decStr) - 1, sizeof(decStr) - 1, decStr); + } + break; + case SQL_TEXT: str2 = var->value.asChar; // See if it is character set OCTETS @@ -7949,6 +8049,7 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns namelength = NULL_DISP_LEN; const unsigned type = var.type; + const SSHORT charSet = TTYPE_TO_CHARSET(var.charSet); switch (type) { @@ -7976,26 +8077,32 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns case SQL_DOUBLE: disp_length = DOUBLE_LEN; break; + case SQL_DEC16: + disp_length = Firebird::IDecFloat16::STRING_SIZE - 1; + break; + case SQL_DEC34: + disp_length = Firebird::IDecFloat34::STRING_SIZE - 1; + break; case SQL_TEXT: alignment = 1; data_length++; // OCTETS data is displayed in hex - if (var.charSet == CS_BINARY) + if (charSet == CS_BINARY) disp_length = 2 * var.length; - else if (var.charSet == CS_UNICODE_FSS) + else if (charSet == CS_UNICODE_FSS) disp_length /= 3; - else if (var.charSet == CS_UTF8) + else if (charSet == CS_UTF8) disp_length /= 4; break; case SQL_VARYING: data_length += sizeof(USHORT) + 1; alignment = sizeof(USHORT); // OCTETS data is displayed in hex - if (var.charSet == CS_BINARY) + if (charSet == CS_BINARY) disp_length = 2 * var.length; - else if (var.charSet == CS_UNICODE_FSS) + else if (charSet == CS_UNICODE_FSS) disp_length /= 3; - else if (var.charSet == CS_UTF8) + else if (charSet == CS_UTF8) disp_length /= 4; break; case SQL_SHORT: @@ -8037,7 +8144,7 @@ static unsigned process_message_display(Firebird::IMessageMetadata* message, uns pad[i] = setValues.global_Col_default; disp_length = pad[i]; - if (var.charSet == 4) + if (charSet == CS_UTF8) disp_length *= 4; } @@ -8628,6 +8735,10 @@ static const char* sqltype_to_string(unsigned sqltype) return "FLOAT"; case SQL_DOUBLE: return "DOUBLE"; + case SQL_DEC16: + return "DECFLOAT(16)"; + case SQL_DEC34: + return "DECFLOAT(34)"; case SQL_D_FLOAT: return "D_FLOAT"; case SQL_TIMESTAMP: diff --git a/src/isql/isql.h b/src/isql/isql.h index 37142759b8..328754d10b 100644 --- a/src/isql/isql.h +++ b/src/isql/isql.h @@ -35,6 +35,7 @@ #include "../jrd/flags.h" #include +#include // Define lengths used in isql.e @@ -294,6 +295,8 @@ const int BLOB = 261; //const int SQL_TIME = 13; const int BIGINT = 16; const int BOOLEAN_TYPE = 23; +const int DEC64_TYPE = 24; +const int DEC128_TYPE = 25; static const sqltypes Column_types[] = { {SMALLINT, "SMALLINT"}, // keyword @@ -311,6 +314,8 @@ static const sqltypes Column_types[] = { {blr_timestamp, "TIMESTAMP"}, // keyword {BIGINT, "BIGINT"}, // keyword {BOOLEAN_TYPE, "BOOLEAN"}, // keyword + {DEC64_TYPE, "DECFLOAT(16)"}, + {DEC128_TYPE, "DECFLOAT(34)"}, {0, ""} }; @@ -397,8 +402,12 @@ public: USHORT major_ods; USHORT minor_ods; USHORT att_charset; + Firebird::IDecFloat16* df16; + Firebird::IDecFloat34* df34; void printf(const char* buffer, ...); void prints(const char* buffer); + + IsqlGlobals(); }; extern IsqlGlobals isqlGlob; @@ -452,6 +461,8 @@ struct IsqlVar ISC_QUAD* blobid; vary* asVary; char* asChar; + FB_DEC16* asDec16; + FB_DEC34* asDec34; void* setPtr; }; TypeMix value; diff --git a/src/isql/show.epp b/src/isql/show.epp index ef31406b40..d9b0edddd4 100644 --- a/src/isql/show.epp +++ b/src/isql/show.epp @@ -3845,7 +3845,7 @@ static processing_state show_domains(const SCHAR* domain_name) first = false; // Print the name of the domain fb_utils::exact_name(FLD.RDB$FIELD_NAME); - isqlGlob.printf("%-32s", FLD.RDB$FIELD_NAME); + isqlGlob.printf("%-31s ", FLD.RDB$FIELD_NAME); // Array dimensions if (!FLD.RDB$DIMENSIONS.NULL) @@ -4841,7 +4841,7 @@ static processing_state show_packages(const SCHAR* package_name) first = false; // Print the name of the package fb_utils::exact_name(PACK.RDB$PACKAGE_NAME); - isqlGlob.printf("%-32s", PACK.RDB$PACKAGE_NAME); + isqlGlob.printf("%-31s ", PACK.RDB$PACKAGE_NAME); isqlGlob.printf(NEWLINE); diff --git a/src/jrd/Attachment.cpp b/src/jrd/Attachment.cpp index 22d3ec93c2..0070a800c9 100644 --- a/src/jrd/Attachment.cpp +++ b/src/jrd/Attachment.cpp @@ -203,6 +203,7 @@ Jrd::Attachment::Attachment(MemoryPool* pool, Database* dbb) att_functions(*pool), att_internal(*pool), att_dyn_req(*pool), + att_dec_status(DEC_Errors), att_charsets(*pool), att_charset_ids(*pool), att_pools(*pool), diff --git a/src/jrd/Attachment.h b/src/jrd/Attachment.h index 1989731f37..4795bfc5c5 100644 --- a/src/jrd/Attachment.h +++ b/src/jrd/Attachment.h @@ -349,6 +349,8 @@ public: Firebird::Array att_internal; // internal statements Firebird::Array att_dyn_req; // internal dyn statements Firebird::ICryptKeyCallback* att_crypt_callback; // callback for DB crypt + Firebird::DecimalStatus att_dec_status; // error handling and rounding + Firebird::DecimalBinding att_dec_binding; // use legacy datatype for DecFloat in outer world jrd_req* findSystemRequest(thread_db* tdbb, USHORT id, USHORT which); diff --git a/src/jrd/CryptoManager.cpp b/src/jrd/CryptoManager.cpp index 5a7e70702c..dc0024f990 100644 --- a/src/jrd/CryptoManager.cpp +++ b/src/jrd/CryptoManager.cpp @@ -812,7 +812,7 @@ namespace Jrd { if (!down) { RefPtr jAtt(REF_NO_INCR, dbb.dbb_provider->attachDatabase(&status_vector, - dbb.dbb_filename.c_str(), writer.getBufferLength(), writer.getBuffer())); + dbb.dbb_database_name.c_str(), writer.getBufferLength(), writer.getBuffer())); check(&status_vector); MutexLockGuard attGuard(*(jAtt->getStable()->getMutex()), FB_FUNCTION); diff --git a/src/jrd/DataTypeUtil.cpp b/src/jrd/DataTypeUtil.cpp index c88c0f89ec..8024d47373 100644 --- a/src/jrd/DataTypeUtil.cpp +++ b/src/jrd/DataTypeUtil.cpp @@ -306,7 +306,7 @@ void DataTypeUtilBase::makeSubstr(dsc* result, const dsc* value, const dsc* offs if (length->dsc_address) // constant { - SLONG constant = CVT_get_long(length, 0, ERR_post); + SLONG constant = CVT_get_long(length, 0, JRD_get_thread_data()->getAttachment()->att_dec_status, ERR_post); fb_assert(constant >= 0); len = MIN(len, MIN(MAX_STR_SIZE, ULONG(constant)) * maxBytesPerChar(result->getCharSet())); } diff --git a/src/jrd/DbCreators.cpp b/src/jrd/DbCreators.cpp index fea7c0dcc3..57af406871 100644 --- a/src/jrd/DbCreators.cpp +++ b/src/jrd/DbCreators.cpp @@ -73,6 +73,7 @@ bool openDb(const char* securityDb, RefPtr& att, RefPtr >& aParameters, const Format* aFormat) : MessageNode(pool), parameters(aParameters), - format(aFormat), - isSpecial(pool) + format(aFormat) { setup(tdbb, csb, message, format->fmt_count); } @@ -104,13 +103,34 @@ namespace if (!param->prm_nullable) itemInfo->nullable = false; - - isSpecial.getBuffer(index / 2 + 1)[index / 2] = itemInfo->isSpecial(); } return type_alignments[desc->dsc_dtype]; } + public: + Array >& parameters; + const Format* format; + }; + + // External message node. + class ExtMessageNode : public MessageNode + { + public: + ExtMessageNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, USHORT message, const Format* aFormat) + : MessageNode(pool), + format(aFormat) + { + setup(tdbb, csb, message, format->fmt_count); + } + + virtual USHORT setupDesc(thread_db* tdbb, CompilerScratch* csb, USHORT index, + dsc* desc, ItemInfo* itemInfo) + { + *desc = format->fmt_desc[index]; + return type_alignments[desc->dsc_dtype]; + } + virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const { if (request->req_operation == jrd_req::req_evaluate) @@ -124,32 +144,34 @@ namespace } public: - Array >& parameters; const Format* format; - Array isSpecial; }; // Initialize output parameters with their domains default value or NULL. // Kind of blr_init_variable, but for parameters. - class ExtInitParameterNode : public TypedNode + class InitParameterNode : public TypedNode { public: - ExtInitParameterNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, - ExtMessageNode* aMessage, USHORT aArgNumber) + InitParameterNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, + Array >& parameters, MessageNode* aMessage, USHORT aArgNumber) : TypedNode(pool), message(aMessage), argNumber(aArgNumber) { - Parameter* parameter = message->parameters[argNumber / 2]; + Parameter* parameter = parameters[argNumber / 2]; + defaultValueNode = NULL; - if (parameter->prm_mechanism == prm_mech_type_of || - fb_utils::implicit_domain(parameter->prm_field_source.c_str()) || - !parameter->prm_default_value) + if (parameter->prm_mechanism != prm_mech_type_of && + !fb_utils::implicit_domain(parameter->prm_field_source.c_str())) { - defaultValueNode = NULL; + MetaNamePair namePair(parameter->prm_field_source, ""); + + FieldInfo fieldInfo; + bool exist = csb->csb_map_field_info.get(namePair, fieldInfo); + + if (exist && fieldInfo.defaultValue) + defaultValueNode = CMP_clone_node(tdbb, csb, fieldInfo.defaultValue); } - else - defaultValueNode = CMP_clone_node(tdbb, csb, parameter->prm_default_value); } string internalPrint(NodePrinter& printer) const @@ -160,20 +182,20 @@ namespace NODE_PRINT(printer, argNumber); NODE_PRINT(printer, defaultValueNode); - return "ExtInitParameterNode"; + return "InitParameterNode"; } void genBlr(DsqlCompilerScratch* /*dsqlScratch*/) { } - ExtInitParameterNode* pass1(thread_db* tdbb, CompilerScratch* csb) + InitParameterNode* pass1(thread_db* tdbb, CompilerScratch* csb) { doPass1(tdbb, csb, &defaultValueNode); return this; } - ExtInitParameterNode* pass2(thread_db* tdbb, CompilerScratch* csb) + InitParameterNode* pass2(thread_db* tdbb, CompilerScratch* csb) { ExprNode::doPass2(tdbb, csb, &defaultValueNode); return this; @@ -224,48 +246,45 @@ namespace } private: - ExtMessageNode* message; + MessageNode* message; USHORT argNumber; ValueExprNode* defaultValueNode; }; - // External output parameters initialization. - class ExtInitOutputNode : public CompoundStmtNode + // Output parameters initialization. + class InitOutputNode : public CompoundStmtNode { public: - ExtInitOutputNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, - ExtMessageNode* message) + InitOutputNode(thread_db* tdbb, MemoryPool& pool, CompilerScratch* csb, + Array >& parameters, MessageNode* message) : CompoundStmtNode(pool) { // Iterate over the format items, except the EOF item. for (USHORT i = 0; i < (message->format->fmt_count / 2) * 2; i += 2) { - ExtInitParameterNode* init = FB_NEW_POOL(pool) ExtInitParameterNode( - tdbb, pool, csb, message, i); + InitParameterNode* init = FB_NEW_POOL(pool) InitParameterNode( + tdbb, pool, csb, parameters, message, i); statements.add(init); } } }; - // External parameters validation. - class ExtValidationNode : public CompoundStmtNode + // Move parameters from a message to another, validating theirs values. + class MessageMoverNode : public CompoundStmtNode { public: - ExtValidationNode(MemoryPool& pool, ExtMessageNode* message, bool procedure, bool input) + MessageMoverNode(MemoryPool& pool, MessageNode* fromMessage, MessageNode* toMessage) : CompoundStmtNode(pool) { // Iterate over the format items, except the EOF item. - for (USHORT i = 0; i < (message->format->fmt_count / 2) * 2; i += 2) + for (USHORT i = 0; i < (fromMessage->format->fmt_count / 2) * 2; i += 2) { - if (!message->isSpecial[i / 2]) - continue; - ParameterNode* flag = FB_NEW_POOL(pool) ParameterNode(pool); - flag->message = message; + flag->message = fromMessage; flag->argNumber = i + 1; ParameterNode* param = FB_NEW_POOL(pool) ParameterNode(pool); - param->message = message; + param->message = fromMessage; param->argNumber = i; param->argFlag = flag; @@ -273,28 +292,17 @@ namespace assign->asgnFrom = param; statements.add(assign); - // It's sufficient to assign input parameters to NULL, but output parameters - // need to be assigned to themselves to validate correctly. - if (input) - assign->asgnTo = FB_NEW_POOL(pool) NullNode(pool); - else - { - flag = FB_NEW_POOL(pool) ParameterNode(pool); - flag->message = message; - flag->argNumber = i + 1; + flag = FB_NEW_POOL(pool) ParameterNode(pool); + flag->message = toMessage; + flag->argNumber = i + 1; - param = FB_NEW_POOL(pool) ParameterNode(pool); - param->message = message; - param->argNumber = i; - param->argFlag = flag; + param = FB_NEW_POOL(pool) ParameterNode(pool); + param->message = toMessage; + param->argNumber = i; + param->argFlag = flag; - assign->asgnTo = param; - } + assign->asgnTo = param; } - - // A stall is required to read req_proc_fetch state correctly. - if (procedure) - statements.add(FB_NEW_POOL(pool) StallNode(pool)); } }; @@ -302,10 +310,11 @@ namespace class ExtFunctionNode : public SuspendNode { public: - ExtFunctionNode(MemoryPool& pool, const ReceiveNode* aReceiveNode, + ExtFunctionNode(MemoryPool& pool, const MessageNode* aExtInMessageNode, const MessageNode* aExtOutMessageNode, const ExtEngineManager::Function* aFunction) : SuspendNode(pool), - receiveNode(aReceiveNode), + extInMessageNode(aExtInMessageNode), + extOutMessageNode(aExtOutMessageNode), function(aFunction) { } @@ -314,9 +323,8 @@ namespace { if (request->req_operation == jrd_req::req_evaluate) { - UCHAR* inMsg = receiveNode ? - request->getImpure(receiveNode->message->impureOffset) : NULL; - UCHAR* outMsg = request->getImpure(message->impureOffset); + UCHAR* inMsg = extInMessageNode ? request->getImpure(extInMessageNode->impureOffset) : NULL; + UCHAR* outMsg = request->getImpure(extOutMessageNode->impureOffset); function->execute(tdbb, inMsg, outMsg); } @@ -325,83 +333,87 @@ namespace } private: - const ReceiveNode* receiveNode; + const MessageNode* extInMessageNode; + const MessageNode* extOutMessageNode; const ExtEngineManager::Function* function; }; // External procedure node. - class ExtProcedureNode : public SuspendNode + class ExtProcedureNode : public CompoundStmtNode { public: - ExtProcedureNode(MemoryPool& pool, const ReceiveNode* aReceiveNode, - const ExtEngineManager::Procedure* aProcedure) - : SuspendNode(pool), - receiveNode(aReceiveNode), + ExtProcedureNode(MemoryPool& pool, MessageNode* aExtInMessageNode, MessageNode* aExtOutMessageNode, + MessageNode* aIntOutMessageNode, const ExtEngineManager::Procedure* aProcedure) + : CompoundStmtNode(pool), + extInMessageNode(aExtInMessageNode), + extOutMessageNode(aExtOutMessageNode), + intOutMessageNode(aIntOutMessageNode), procedure(aProcedure) { + SuspendNode* suspend = FB_NEW_POOL(pool) SuspendNode(pool); + suspend->message = intOutMessageNode; + suspend->statement = FB_NEW_POOL(getPool()) MessageMoverNode(pool, extOutMessageNode, intOutMessageNode); + + statements.add(suspend); + statements.add(FB_NEW_POOL(getPool()) StallNode(pool)); } virtual const StmtNode* execute(thread_db* tdbb, jrd_req* request, ExeState* exeState) const { + impure_state* const impure = request->getImpure(impureOffset); ExtEngineManager::ResultSet*& resultSet = request->req_ext_resultset; - UCHAR* inMsg = receiveNode ? - request->getImpure(receiveNode->message->impureOffset) : NULL; - UCHAR* outMsg = request->getImpure(message->impureOffset); - USHORT* eof = (USHORT*) (outMsg + (IPTR) message->format->fmt_desc.back().dsc_address); + UCHAR* extInMsg = extInMessageNode ? request->getImpure(extInMessageNode->impureOffset) : NULL; + UCHAR* extOutMsg = extOutMessageNode ? request->getImpure(extOutMessageNode->impureOffset) : NULL; + UCHAR* intOutMsg = intOutMessageNode ? request->getImpure(intOutMessageNode->impureOffset) : NULL; + SSHORT* eof = intOutMsg ? + (SSHORT*) (intOutMsg + (IPTR) intOutMessageNode->format->fmt_desc.back().dsc_address) : NULL; switch (request->req_operation) { case jrd_req::req_evaluate: - fb_assert(!resultSet && *eof == 0); - resultSet = procedure->open(tdbb, inMsg, outMsg); + fb_assert(!resultSet); + resultSet = procedure->open(tdbb, extInMsg, extOutMsg); - if (resultSet) - *eof = -1; - else + if (!resultSet) { - if (!(request->req_flags & req_proc_fetch)) + *eof = 0; + break; + } + else + *eof = -1; + // fall into + + case jrd_req::req_proceed: + case jrd_req::req_sync: + if (resultSet) + { + if (resultSet->fetch(tdbb) && (request->req_flags & req_proc_fetch)) + *eof = -1; + else { - request->req_operation = jrd_req::req_evaluate; - return statement; + *eof = 0; + delete resultSet; + resultSet = NULL; } } - request->req_operation = jrd_req::req_return; - // fall into - - case jrd_req::req_return: - if (*eof == 0) - { - fb_assert(!resultSet); - return parentStmt; - } - - fb_assert(resultSet); - - if (!resultSet->fetch(tdbb) || !(request->req_flags & req_proc_fetch)) - { - *eof = 0; - delete resultSet; - resultSet = NULL; - } - + impure->sta_state = 0; // suspend node + request->req_operation = jrd_req::req_sync; break; - case jrd_req::req_proceed: - request->req_operation = jrd_req::req_evaluate; - return statement; - case jrd_req::req_unwind: delete resultSet; resultSet = NULL; break; } - return SuspendNode::execute(tdbb, request, exeState); + return CompoundStmtNode::execute(tdbb, request, exeState); } private: - const ReceiveNode* receiveNode; + MessageNode* extInMessageNode; + MessageNode* extOutMessageNode; + MessageNode* intOutMessageNode; const ExtEngineManager::Procedure* procedure; }; @@ -628,10 +640,10 @@ void ExtEngineManager::ExternalContextImpl::setTransaction(thread_db* tdbb) if ((internalTransaction = newTransaction)) { - internalTransaction->getInterface()->addRef(); + internalTransaction->getInterface(true)->addRef(); externalTransaction = MasterInterfacePtr()->registerTransaction(externalAttachment, - internalTransaction->getInterface()); + internalTransaction->getInterface(true)); } } @@ -776,7 +788,8 @@ ExtEngineManager::ResultSet::ResultSet(thread_db* tdbb, UCHAR* inMsg, UCHAR* out firstFetch(true) { attInfo = procedure->extManager->getEngineAttachment(tdbb, procedure->engine); - const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? procedure->prc->owner : ""; + const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? + procedure->prc->owner : ""; ContextManager ctxManager(tdbb, attInfo, procedure->procedure, (procedure->prc->getName().package.isEmpty() ? CallerName(obj_procedure, procedure->prc->getName().identifier, userName) : @@ -810,7 +823,8 @@ bool ExtEngineManager::ResultSet::fetch(thread_db* tdbb) if (!resultSet) return wasFirstFetch; - const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? procedure->prc->owner : ""; + const MetaName& userName = procedure->prc->ssDefiner.specified && procedure->prc->ssDefiner.value ? + procedure->prc->owner : ""; ContextManager ctxManager(tdbb, attInfo, charSet, (procedure->prc->getName().package.isEmpty() ? CallerName(obj_procedure, procedure->prc->getName().identifier, userName) : @@ -1078,6 +1092,7 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: status.check(); IExternalFunction* externalFunction; + RefPtr extInputParameters, extOutputParameters; { // scope EngineCheckout cout(tdbb, FB_FUNCTION); @@ -1092,16 +1107,19 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: Arg::Gds(isc_eem_func_not_returned) << udf->getName().toString() << engine); } - metadata->inputParameters = inBuilder->getMetadata(&status); + extInputParameters = inBuilder->getMetadata(&status); status.check(); - metadata->outputParameters = outBuilder->getMetadata(&status); + extOutputParameters = outBuilder->getMetadata(&status); status.check(); } udf->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); udf->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); + const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false); + const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true); + try { udf->fun_external = FB_NEW_POOL(getPool()) Function(tdbb, this, attInfo->engine, @@ -1109,38 +1127,48 @@ void ExtEngineManager::makeFunction(thread_db* tdbb, CompilerScratch* csb, Jrd:: CompoundStmtNode* mainNode = FB_NEW_POOL(getPool()) CompoundStmtNode(getPool()); - ExtMessageNode* inMessageNode = udf->getInputFields().hasData() ? - FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 0, + IntMessageNode* intInMessageNode = udf->getInputFields().hasData() ? + FB_NEW_POOL(getPool()) IntMessageNode(tdbb, getPool(), csb, 0, udf->getInputFields(), udf->getInputFormat()) : NULL; - if (inMessageNode) - mainNode->statements.add(inMessageNode); + ExtMessageNode* extInMessageNode = NULL; - ExtMessageNode* outMessageNode = FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 1, + if (intInMessageNode) + { + mainNode->statements.add(intInMessageNode); + + extInMessageNode = FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 2, extInputFormat); + mainNode->statements.add(extInMessageNode); + } + + IntMessageNode* intOutMessageNode = FB_NEW_POOL(getPool()) IntMessageNode(tdbb, getPool(), csb, 1, udf->getOutputFields(), udf->getOutputFormat()); - mainNode->statements.add(outMessageNode); + mainNode->statements.add(intOutMessageNode); - ExtInitOutputNode* initOutputNode = FB_NEW_POOL(getPool()) ExtInitOutputNode( - tdbb, getPool(), csb, outMessageNode); + ExtMessageNode* extOutMessageNode = FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 3, + extOutputFormat); + mainNode->statements.add(extOutMessageNode); + + // Initialize the output fields into the external message. + InitOutputNode* initOutputNode = FB_NEW_POOL(getPool()) InitOutputNode( + tdbb, getPool(), csb, udf->getOutputFields(), extOutMessageNode); mainNode->statements.add(initOutputNode); - ReceiveNode* receiveNode = inMessageNode ? - FB_NEW_POOL(getPool()) ReceiveNode(getPool()) : NULL; - - if (inMessageNode) + if (intInMessageNode) { - receiveNode->message = inMessageNode; - receiveNode->statement = FB_NEW_POOL(getPool()) ExtValidationNode( - getPool(), inMessageNode, false, true); + ReceiveNode* receiveNode = intInMessageNode ? FB_NEW_POOL(getPool()) ReceiveNode(getPool()) : NULL; + receiveNode->message = intInMessageNode; + receiveNode->statement = FB_NEW_POOL(getPool()) MessageMoverNode( + getPool(), intInMessageNode, extInMessageNode); mainNode->statements.add(receiveNode); } ExtFunctionNode* extFunctionNode = FB_NEW_POOL(getPool()) ExtFunctionNode(getPool(), - receiveNode, udf->fun_external); + extInMessageNode, extOutMessageNode, udf->fun_external); mainNode->statements.add(extFunctionNode); - extFunctionNode->message = outMessageNode; - extFunctionNode->statement = FB_NEW_POOL(getPool()) ExtValidationNode( - getPool(), outMessageNode, false, false); + extFunctionNode->message = intOutMessageNode; + extFunctionNode->statement = FB_NEW_POOL(getPool()) MessageMoverNode( + getPool(), extOutMessageNode, intOutMessageNode); JrdStatement* statement = udf->getStatement(); PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); @@ -1188,6 +1216,7 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ status.check(); IExternalProcedure* externalProcedure; + RefPtr extInputParameters, extOutputParameters; { // scope EngineCheckout cout(tdbb, FB_FUNCTION); @@ -1203,16 +1232,19 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ prc->getName().toString() << engine); } - metadata->inputParameters = inBuilder->getMetadata(&status); + extInputParameters = inBuilder->getMetadata(&status); status.check(); - metadata->outputParameters = outBuilder->getMetadata(&status); + extOutputParameters = outBuilder->getMetadata(&status); status.check(); } prc->setInputFormat(Routine::createFormat(pool, metadata->inputParameters, false)); prc->setOutputFormat(Routine::createFormat(pool, metadata->outputParameters, true)); + const Format* extInputFormat = Routine::createFormat(pool, extInputParameters, false); + const Format* extOutputFormat = Routine::createFormat(pool, extOutputParameters, true); + try { prc->setExternal(FB_NEW_POOL(getPool()) Procedure(tdbb, this, attInfo->engine, @@ -1220,38 +1252,54 @@ void ExtEngineManager::makeProcedure(thread_db* tdbb, CompilerScratch* csb, jrd_ CompoundStmtNode* mainNode = FB_NEW_POOL(getPool()) CompoundStmtNode(getPool()); - ExtMessageNode* inMessageNode = prc->getInputFields().hasData() ? - FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 0, + IntMessageNode* intInMessageNode = prc->getInputFields().hasData() ? + FB_NEW_POOL(getPool()) IntMessageNode(tdbb, getPool(), csb, 0, prc->getInputFields(), prc->getInputFormat()) : NULL; - if (inMessageNode) - mainNode->statements.add(inMessageNode); + ExtMessageNode* extInMessageNode = NULL; - ExtMessageNode* outMessageNode = FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 1, - prc->getOutputFields(), prc->getOutputFormat()); - mainNode->statements.add(outMessageNode); - - ExtInitOutputNode* initOutputNode = FB_NEW_POOL(getPool()) ExtInitOutputNode( - tdbb, getPool(), csb, outMessageNode); - mainNode->statements.add(initOutputNode); - - ReceiveNode* receiveNode = inMessageNode ? - FB_NEW_POOL(getPool()) ReceiveNode(getPool()) : NULL; - - if (inMessageNode) + if (intInMessageNode) { - receiveNode->message = inMessageNode; - receiveNode->statement = FB_NEW_POOL(getPool()) ExtValidationNode( - getPool(), inMessageNode, true, true); - mainNode->statements.add(receiveNode); + mainNode->statements.add(intInMessageNode); + + extInMessageNode = FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), csb, 2, extInputFormat); + mainNode->statements.add(extInMessageNode); } + IntMessageNode* intOutMessageNode = FB_NEW_POOL(getPool()) IntMessageNode(tdbb, getPool(), csb, 1, + prc->getOutputFields(), prc->getOutputFormat()); + mainNode->statements.add(intOutMessageNode); + + ExtMessageNode* extOutMessageNode = FB_NEW_POOL(getPool()) ExtMessageNode(tdbb, getPool(), + csb, 3, extOutputFormat); + mainNode->statements.add(extOutMessageNode); + + // Initialize the output fields into the external message. + InitOutputNode* initOutputNode = FB_NEW_POOL(getPool()) InitOutputNode( + tdbb, getPool(), csb, prc->getOutputFields(), extOutMessageNode); + mainNode->statements.add(initOutputNode); + + ReceiveNode* receiveNode = intInMessageNode ? + FB_NEW_POOL(getPool()) ReceiveNode(getPool()) : NULL; + + if (intInMessageNode) + { + CompoundStmtNode* receiveSubStatement = FB_NEW_POOL(getPool()) CompoundStmtNode(getPool()); + receiveSubStatement->statements.add(FB_NEW_POOL(getPool()) MessageMoverNode( + getPool(), intInMessageNode, extInMessageNode)); + receiveSubStatement->statements.add(FB_NEW_POOL(getPool()) StallNode(getPool())); + + receiveNode->statement = receiveSubStatement; + receiveNode->message = intInMessageNode; + + mainNode->statements.add(receiveNode); + } + else + mainNode->statements.add(FB_NEW_POOL(getPool()) StallNode(getPool())); + ExtProcedureNode* extProcedureNode = FB_NEW_POOL(getPool()) ExtProcedureNode(getPool(), - receiveNode, prc->getExternal()); + extInMessageNode, extOutMessageNode, intOutMessageNode, prc->getExternal()); mainNode->statements.add(extProcedureNode); - extProcedureNode->message = outMessageNode; - extProcedureNode->statement = FB_NEW_POOL(getPool()) ExtValidationNode( - getPool(), outMessageNode, true, false); JrdStatement* statement = prc->getStatement(); PAR_preparsed_node(tdbb, NULL, mainNode, NULL, &csb, &statement, false, 0); diff --git a/src/jrd/JrdStatement.cpp b/src/jrd/JrdStatement.cpp index ce8ec26b9e..78295afd5b 100644 --- a/src/jrd/JrdStatement.cpp +++ b/src/jrd/JrdStatement.cpp @@ -761,7 +761,7 @@ template static void makeSubRoutines(thread_db* tdbb, JrdStatement* Routine* subRoutine = subNode->routine; CompilerScratch*& subCsb = subNode->subCsb; - JrdStatement* subStatement = JrdStatement::makeStatement(tdbb, subCsb, true); + JrdStatement* subStatement = JrdStatement::makeStatement(tdbb, subCsb, false); subStatement->parentStatement = statement; subRoutine->setStatement(subStatement); diff --git a/src/jrd/Mapping.cpp b/src/jrd/Mapping.cpp index 4fbca24a92..254b353118 100644 --- a/src/jrd/Mapping.cpp +++ b/src/jrd/Mapping.cpp @@ -32,10 +32,7 @@ #include "gen/iberror.h" #include "../jrd/constants.h" -#include "../common/classes/ClumpletWriter.h" #include "../common/classes/init.h" -#include "../common/classes/Hash.h" -#include "../common/classes/GenericMap.h" #include "../common/classes/RefMutex.h" #include "../common/classes/SyncObject.h" #include "../common/classes/MetaName.h" @@ -62,11 +59,15 @@ using namespace Jrd; namespace { -const unsigned FLAG_DB = 1; -const unsigned FLAG_SEC = 2; +// internalFlags bits +const ULONG FLAG_DB = 1; +const ULONG FLAG_SEC = 2; +const ULONG FLAG_DOWN_DB = 4; +const ULONG FLAG_DOWN_SEC = 8; -const unsigned FLAG_USER = 1; -const unsigned FLAG_ROLE = 2; +// flagRolUsr values +const ULONG FLAG_USER = 1; +const ULONG FLAG_ROLE = 2; const char* NM_ROLE = "Role"; const char* NM_USER = "User"; @@ -82,6 +83,10 @@ void check(const char* s, IStatus* st) newStatus.raise(); } +} // anonymous namespace + +namespace Jrd { + class AuthWriter : public ClumpletWriter { public: @@ -145,377 +150,415 @@ private: unsigned char sequence; }; -class Map; -typedef HashTable, Map> MapHash; +} // namespace Jrd -class Map : public MapHash::Entry, public GlobalStorage + +Mapping::DbHandle::DbHandle() +{ } + +void Mapping::DbHandle::setAttachment(IAttachment* att) { -public: - static FB_SIZE_T hash(const Map& value, FB_SIZE_T hashSize) + if (att) { - NoCaseString key = value.makeHashKey(); - return DefaultHash::hash(key.c_str(), key.length(), hashSize); + MAP_DEBUG(fprintf(stderr, "Using existing db handle %p\n", att)); + assign(att); } +} - NoCaseString makeHashKey() const - { - NoCaseString key; - key += usng; - MAP_DEBUG(key += ':'); - key += plugin; - MAP_DEBUG(key += ':'); - key += db; - MAP_DEBUG(key += ':'); - key += fromType; - MAP_DEBUG(key += ':'); - key += from; - - key.upper(); - return key; - } - - NoCaseString plugin, db, fromType, from, to; - bool toRole; - char usng; - - Map(const char* aUsing, const char* aPlugin, const char* aDb, - const char* aFromType, const char* aFrom, - SSHORT aRole, const char* aTo) - : plugin(getPool()), db(getPool()), fromType(getPool()), - from(getPool()), to(getPool()), toRole(aRole ? true : false), usng(aUsing[0]) - { - plugin = aPlugin; - db = aDb; - fromType = aFromType; - from = aFrom; - to = aTo; - - trimAll(); - } - - explicit Map(AuthReader::Info& info) //type, name, plugin, secDb - : plugin(getPool()), db(getPool()), - fromType(getPool()), from(getPool()), to(getPool()), - toRole(false), usng(info.plugin.hasData() ? 'P' : 'M') - { - plugin = info.plugin.hasData() ? info.plugin.c_str() : "*"; - db = info.secDb.hasData() ? info.secDb.c_str() : "*"; - fromType = info.type; - from = info.name.hasData() ? info.name.c_str() : "*"; - - trimAll(); - } - - void trimAll() - { - plugin.rtrim(); - db.rtrim(); - fromType.rtrim(); - from.rtrim(); - to.rtrim(); - } - - virtual bool isEqual(const Map& k) const - { - return usng == k.usng && - plugin == k.plugin && - db == k.db && - fromType == k.fromType && - from == k.from ; - } - - virtual Map* get() - { - return this; - } -}; - -class Cache : public MapHash, public GlobalStorage +void Mapping::DbHandle::clear() { -public: - Cache(const NoCaseString& aliasDb, const NoCaseString& db) - : alias(getPool(), aliasDb), name(getPool(), db), - dataFlag(false), downFlag(false) + assign(nullptr); +} + +bool Mapping::DbHandle::attach(const char* aliasDb, ICryptKeyCallback* cryptCb) +{ + FbLocalStatus st; + bool down = false; // true if on attach db is shutdown + + if (hasData()) { - enableDuplicates(); + MAP_DEBUG(fprintf(stderr, "Already attached %s\n", aliasDb)); + return down; } - void populate(IAttachment *att, bool isDown) + DispatcherPtr prov; + if (cryptCb) { - FbLocalStatus st; + prov->setDbCryptCallback(&st, cryptCb); + check("IProvider::setDbCryptCallback", &st); + } - if (dataFlag) + ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, 1024, isc_dpb_version1); + embeddedSysdba.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); + embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); + embeddedSysdba.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); + embeddedSysdba.insertByte(isc_dpb_map_attach, TRUE); + embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); + + MAP_DEBUG(fprintf(stderr, "Attach %s\n", aliasDb)); + IAttachment* att = prov->attachDatabase(&st, aliasDb, + embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); + + if (st->getState() & IStatus::STATE_ERRORS) + { + const ISC_STATUS* s = st->getErrors(); + MAP_DEBUG(isc_print_status(s)); + bool missing = fb_utils::containsErrorCode(s, isc_io_error); + down = fb_utils::containsErrorCode(s, isc_shutdown); + if (!(missing || down)) + check("IProvider::attachDatabase", &st); + + // down/missing security DB is not a reason to fail mapping + } + else + assignRefNoIncr(att); + + MAP_DEBUG(fprintf(stderr, "Att=%p\n", att)); + + return down; +} + + +FB_SIZE_T Mapping::Map::hash(const Map& value, FB_SIZE_T hashSize) +{ + NoCaseString key = value.makeHashKey(); + return DefaultHash::hash(key.c_str(), key.length(), hashSize); +} + +NoCaseString Mapping::Map::makeHashKey() const +{ + NoCaseString key; + key += usng; + MAP_DEBUG(key += ':'); + key += plugin; + MAP_DEBUG(key += ':'); + key += db; + MAP_DEBUG(key += ':'); + key += fromType; + MAP_DEBUG(key += ':'); + key += from; + + key.upper(); + return key; +} + +Mapping::Map::Map(const char* aUsing, const char* aPlugin, const char* aDb, + const char* aFromType, const char* aFrom, + SSHORT aRole, const char* aTo) + : plugin(getPool()), db(getPool()), fromType(getPool()), + from(getPool()), to(getPool()), toRole(aRole ? true : false), usng(aUsing[0]) +{ + plugin = aPlugin; + db = aDb; + fromType = aFromType; + from = aFrom; + to = aTo; + + trimAll(); +} + +Mapping::Map::Map(AuthReader::Info& info) //type, name, plugin, secDb + : plugin(getPool()), db(getPool()), + fromType(getPool()), from(getPool()), to(getPool()), + toRole(false), usng(info.plugin.hasData() ? 'P' : 'M') +{ + plugin = info.plugin.hasData() ? info.plugin.c_str() : "*"; + db = info.secDb.hasData() ? info.secDb.c_str() : "*"; + fromType = info.type; + from = info.name.hasData() ? info.name.c_str() : "*"; + + trimAll(); +} + +void Mapping::Map::trimAll() +{ + plugin.rtrim(); + db.rtrim(); + fromType.rtrim(); + from.rtrim(); + to.rtrim(); +} + +bool Mapping::Map::isEqual(const Map& k) const +{ + return usng == k.usng && + plugin == k.plugin && + db == k.db && + fromType == k.fromType && + from == k.from ; +} + +Mapping::Map* Mapping::Map::get() +{ + return this; +} + + +Mapping::Cache::Cache(const NoCaseString& aliasDb, const NoCaseString& db) + : alias(getPool(), aliasDb), name(getPool(), db), + dataFlag(false) +{ + enableDuplicates(); +} + +Mapping::Cache::~Cache() +{ + cleanup(eraseEntry); +} + +bool Mapping::Cache::populate(IAttachment *att) +{ + FbLocalStatus st; + + if (dataFlag) + { + return false; + } + + if (!att) + { + dataFlag = true; + return false; + } + + MAP_DEBUG(fprintf(stderr, "Populate cache for %s\n", name.c_str())); + + ITransaction* tra = nullptr; + IResultSet* curs = nullptr; + + try + { + ClumpletWriter readOnly(ClumpletWriter::Tpb, MAX_DPB_SIZE, isc_tpb_version1); + readOnly.insertTag(isc_tpb_read); + readOnly.insertTag(isc_tpb_wait); + tra = att->startTransaction(&st, readOnly.getBufferLength(), readOnly.getBuffer()); + check("IAttachment::startTransaction", &st); + + Message mMap; + Field usng(mMap, 1); + Field plugin(mMap, MAX_SQL_IDENTIFIER_SIZE); + Field db(mMap, MAX_SQL_IDENTIFIER_SIZE); + Field fromType(mMap, MAX_SQL_IDENTIFIER_SIZE); + Field from(mMap, 255); + Field role(mMap); + Field to(mMap, MAX_SQL_IDENTIFIER_SIZE); + + curs = att->openCursor(&st, tra, 0, + "SELECT RDB$MAP_USING, RDB$MAP_PLUGIN, RDB$MAP_DB, RDB$MAP_FROM_TYPE, " + " RDB$MAP_FROM, RDB$MAP_TO_TYPE, RDB$MAP_TO " + "FROM RDB$AUTH_MAPPING", + 3, nullptr, nullptr, mMap.getMetadata(), nullptr, 0); + if (st->getState() & IStatus::STATE_ERRORS) { - return; - } - - if (!att) - { - dataFlag = true; - downFlag = isDown; - return; - } - - MAP_DEBUG(fprintf(stderr, "Populate cache for %s\n", name.c_str())); - - ITransaction* tra = NULL; - IResultSet* curs = NULL; - - try - { - cleanup(eraseEntry); - - ClumpletWriter readOnly(ClumpletWriter::Tpb, MAX_DPB_SIZE, isc_tpb_version1); - readOnly.insertTag(isc_tpb_read); - readOnly.insertTag(isc_tpb_wait); - tra = att->startTransaction(&st, readOnly.getBufferLength(), readOnly.getBuffer()); - check("IAttachment::startTransaction", &st); - - Message mMap; - Field usng(mMap, 1); - Field plugin(mMap, MAX_SQL_IDENTIFIER_SIZE); - Field db(mMap, MAX_SQL_IDENTIFIER_SIZE); - Field fromType(mMap, MAX_SQL_IDENTIFIER_SIZE); - Field from(mMap, 255); - Field role(mMap); - Field to(mMap, MAX_SQL_IDENTIFIER_SIZE); - - curs = att->openCursor(&st, tra, 0, - "SELECT RDB$MAP_USING, RDB$MAP_PLUGIN, RDB$MAP_DB, RDB$MAP_FROM_TYPE, " - " RDB$MAP_FROM, RDB$MAP_TO_TYPE, RDB$MAP_TO " - "FROM RDB$AUTH_MAPPING", - 3, NULL, NULL, mMap.getMetadata(), NULL, 0); - if (st->getState() & IStatus::STATE_ERRORS) + if (fb_utils::containsErrorCode(st->getErrors(), isc_dsql_relation_err)) { - if (fb_utils::containsErrorCode(st->getErrors(), isc_dsql_relation_err)) - { - // isc_dsql_relation_err when opening cursor - i.e. table RDB$AUTH_MAPPING - // is missing due to non-FB3 security DB - tra->release(); - dataFlag = true; - return; - } - check("IAttachment::openCursor", &st); - } - - while (curs->fetchNext(&st, mMap.getBuffer()) == IStatus::RESULT_OK) - { - const char* expandedDb = "*"; - PathName target; - if (!db.null) - { - expandedDb = db; - MAP_DEBUG(fprintf(stderr, "non-expandedDb '%s'\n", expandedDb)); - expandDatabaseName(expandedDb, target, NULL); - expandedDb = target.c_str(); - MAP_DEBUG(fprintf(stderr, "expandedDb '%s'\n", expandedDb)); - } - - Map* map = FB_NEW Map(usng, plugin.null ? "*" : plugin, expandedDb, - fromType, from, role, to.null ? "*" : to); - MAP_DEBUG(fprintf(stderr, "Add = %s\n", map->makeHashKey().c_str())); - add(map); - } - check("IResultSet::fetchNext", &st); - - curs->close(&st); - check("IResultSet::close", &st); - curs = NULL; - - tra->rollback(&st); - check("ITransaction::rollback", &st); - tra = NULL; - - dataFlag = true; - downFlag = false; - } - catch (const Exception& ex) - { - if (curs) - curs->release(); - if (tra) + // isc_dsql_relation_err when opening cursor - i.e. table RDB$AUTH_MAPPING + // is missing due to non-FB3 security DB tra->release(); + dataFlag = true; + return false; + } + check("IAttachment::openCursor", &st); + } - // If database is shutdown it's not a reason to fail mapping - StaticStatusVector status; - ex.stuffException(status); - - const ISC_STATUS* s = status.begin(); - - if (fb_utils::containsErrorCode(s, isc_shutdown)) + while (curs->fetchNext(&st, mMap.getBuffer()) == IStatus::RESULT_OK) + { + const char* expandedDb = "*"; + PathName target; + if (!db.null) { - downFlag = true; - return; + expandedDb = db; + MAP_DEBUG(fprintf(stderr, "non-expandedDb '%s'\n", expandedDb)); + expandDatabaseName(expandedDb, target, nullptr); + expandedDb = target.c_str(); + MAP_DEBUG(fprintf(stderr, "expandedDb '%s'\n", expandedDb)); } - throw; + Map* map = FB_NEW Map(usng, plugin.null ? "*" : plugin, expandedDb, + fromType, from, role, to.null ? "*" : to); + MAP_DEBUG(fprintf(stderr, "Add = %s\n", map->makeHashKey().c_str())); + add(map); } + check("IResultSet::fetchNext", &st); + + curs->close(&st); + check("IResultSet::close", &st); + curs = nullptr; + + tra->rollback(&st); + check("ITransaction::rollback", &st); + tra = nullptr; + + dataFlag = true; + } + catch (const Exception& ex) + { + if (curs) + curs->release(); + if (tra) + tra->release(); + + // If database is shutdown it's not a reason to fail mapping + StaticStatusVector status; + ex.stuffException(status); + + const ISC_STATUS* s = status.begin(); + + if (fb_utils::containsErrorCode(s, isc_shutdown)) + return true; + + throw; } - void map(bool flagWild, AuthReader::Info& info, AuthWriter& newBlock) + return false; +} + +void Mapping::Cache::map(bool flagWild, AuthReader::Info& info, AuthWriter& newBlock) +{ + if (info.type == TYPE_SEEN) + return; + + Map from(info); + + if (from.from == "*") + Arg::Gds(isc_map_aster).raise(); + + if (!flagWild) + search(info, from, newBlock, from.from); + else + varUsing(info, from, newBlock); +} + +void Mapping::Cache::search(AuthReader::Info& info, const Map& from, AuthWriter& newBlock, + const NoCaseString& originalUserName) +{ + MAP_DEBUG(fprintf(stderr, "Key = %s\n", from.makeHashKey().c_str())); + if (!dataFlag) + return; + + for (Map* to = lookup(from); to; to = to->next(from)) { - if (info.type == TYPE_SEEN) - return; + MAP_DEBUG(fprintf(stderr, "Match!!\n")); + unsigned flagRolUsr = to->toRole ? FLAG_ROLE : FLAG_USER; + if (info.found & flagRolUsr) + continue; + if (info.current & flagRolUsr) + (Arg::Gds(isc_map_multi) << originalUserName).raise(); - Map from(info); + info.current |= flagRolUsr; - if (from.from == "*") - Arg::Gds(isc_map_aster).raise(); - - if (!flagWild) - search(info, from, newBlock, from.from); - else - varUsing(info, from, newBlock); + AuthReader::Info newInfo; + newInfo.type = to->toRole ? NM_ROLE : NM_USER; + newInfo.name = to->to == "*" ? originalUserName : to->to; + newInfo.secDb = this->name; + newInfo.origPlug = info.origPlug.hasData() ? info.origPlug : info.plugin; + newBlock.add(newInfo); } +} - void search(AuthReader::Info& info, const Map& from, AuthWriter& newBlock, - const NoCaseString& originalUserName) - { - MAP_DEBUG(fprintf(stderr, "Key = %s\n", from.makeHashKey().c_str())); - if (!dataFlag) - return; - - for (Map* to = lookup(from); to; to = to->next(from)) - { - MAP_DEBUG(fprintf(stderr, "Match!!\n")); - unsigned flagRolUsr = to->toRole ? FLAG_ROLE : FLAG_USER; - if (info.found & flagRolUsr) - continue; - if (info.current & flagRolUsr) - (Arg::Gds(isc_map_multi) << originalUserName).raise(); - - info.current |= flagRolUsr; - - AuthReader::Info newInfo; - newInfo.type = to->toRole ? NM_ROLE : NM_USER; - newInfo.name = to->to == "*" ? originalUserName : to->to; - newInfo.secDb = this->name; - newInfo.origPlug = info.origPlug.hasData() ? info.origPlug : info.plugin; - newBlock.add(newInfo); - } - } - - void varPlugin(AuthReader::Info& info, Map from, AuthWriter& newBlock) +void Mapping::Cache::varPlugin(AuthReader::Info& info, Map from, AuthWriter& newBlock) +{ + varDb(info, from, newBlock); + if (from.plugin != "*") { + from.plugin = "*"; varDb(info, from, newBlock); - if (from.plugin != "*") + } +} + +void Mapping::Cache::varDb(AuthReader::Info& info, Map from, AuthWriter& newBlock) +{ + varFrom(info, from, newBlock); + if (from.db != "*") + { + from.db = "*"; + varFrom(info, from, newBlock); + } +} + +void Mapping::Cache::varFrom(AuthReader::Info& info, Map from, AuthWriter& newBlock) +{ + NoCaseString originalUserName = from.from; + search(info, from, newBlock, originalUserName); + from.from = "*"; + search(info, from, newBlock, originalUserName); +} + +void Mapping::Cache::varUsing(AuthReader::Info& info, Map from, AuthWriter& newBlock) +{ + if (from.usng == 'P') + { + varPlugin(info, from, newBlock); + + from.usng = '*'; + varPlugin(info, from, newBlock); + + if (!info.secDb.hasData()) { + from.usng = 'S'; from.plugin = "*"; varDb(info, from, newBlock); } } - - void varDb(AuthReader::Info& info, Map from, AuthWriter& newBlock) + else if (from.usng == 'M') { - varFrom(info, from, newBlock); - if (from.db != "*") - { - from.db = "*"; - varFrom(info, from, newBlock); - } + varDb(info, from, newBlock); + + from.usng = '*'; + varDb(info, from, newBlock); } + else + fb_assert(false); +} - void varFrom(AuthReader::Info& info, Map from, AuthWriter& newBlock) +bool Mapping::Cache::map4(bool flagWild, unsigned flagSet, AuthReader& rdr, AuthReader::Info& info, AuthWriter& newBlock) +{ + if (!flagSet) { - NoCaseString originalUserName = from.from; - search(info, from, newBlock, originalUserName); - from.from = "*"; - search(info, from, newBlock, originalUserName); - } + AuthWriter workBlock; - void varUsing(AuthReader::Info& info, Map from, AuthWriter& newBlock) - { - if (from.usng == 'P') + for (rdr.rewind(); rdr.getInfo(info); rdr.moveNext()) { - varPlugin(info, from, newBlock); - - from.usng = '*'; - varPlugin(info, from, newBlock); - - if (!info.secDb.hasData()) - { - from.usng = 'S'; - from.plugin = "*"; - varDb(info, from, newBlock); - } - } - else if (from.usng == 'M') - { - varDb(info, from, newBlock); - - from.usng = '*'; - varDb(info, from, newBlock); - } - else - fb_assert(false); - } - - bool map4(bool flagWild, unsigned flagSet, AuthReader& rdr, AuthReader::Info& info, AuthWriter& newBlock) - { - if (!flagSet) - { - AuthWriter workBlock; - - for (rdr.rewind(); rdr.getInfo(info); rdr.moveNext()) - { - map(flagWild, info, workBlock); - } - - info.found |= info.current; - info.current = 0; - newBlock.append(workBlock); + map(flagWild, info, workBlock); } - unsigned mapMask = FLAG_USER | FLAG_ROLE; - return (info.found & mapMask) == mapMask; + info.found |= info.current; + info.current = 0; + newBlock.append(workBlock); } - static void eraseEntry(Map* m) - { - delete m; - } + unsigned mapMask = FLAG_USER | FLAG_ROLE; + return (info.found & mapMask) == mapMask; +} - void makeEmpty() - { - if (!dataFlag) - return; +void Mapping::Cache::eraseEntry(Map* m) +{ + delete m; +} - dataFlag = false; - cleanup(eraseEntry); - } -public: - SyncObject syncObject; - NoCaseString alias, name; - bool dataFlag, downFlag; -}; +namespace +{ -typedef GenericMap > > CacheTree; +typedef GenericMap > > > CacheTree; InitInstance tree; GlobalPtr treeMutex; void setupIpc(); -Cache* locate(const NoCaseString& target) +void locate(RefPtr& cache, const NoCaseString& alias, const NoCaseString& target) { fb_assert(treeMutex->locked()); - Cache* c; - return tree().get(target, c) ? c : NULL; -} - -Cache* locate(const NoCaseString& alias, const NoCaseString& target) -{ - fb_assert(treeMutex->locked()); - Cache* c = locate(target); - if (!c) + fb_assert(!cache); + tree().get(target, cache); + if (!cache) { - c = FB_NEW Cache(alias, target); - *(tree().put(target)) = c; + cache = FB_NEW Mapping::Cache(alias, target); + *(tree().put(target)) = cache; setupIpc(); } - return c; } class Found @@ -550,18 +593,7 @@ public: void resetMap(const char* securityDb) { MutexLockGuard g(treeMutex, FB_FUNCTION); - - Cache* cache = locate(securityDb); - if (!cache) - { - MAP_DEBUG(fprintf(stderr, "Cache not found for %s\n", securityDb)); - return; - } - - Sync sync(&cache->syncObject, FB_FUNCTION); - sync.lock(SYNC_EXCLUSIVE); - cache->makeEmpty(); - MAP_DEBUG(fprintf(stderr, "Empty cache for %s\n", securityDb)); + tree().remove(securityDb); } void resetMap(const char* securityDb, ULONG index); @@ -642,13 +674,13 @@ public: sharedMemory->removeMapFile(); } - sharedMemory = NULL; + sharedMemory = nullptr; } void clearCache(const char* dbName, USHORT index) { PathName target; - expandDatabaseName(dbName, target, NULL); + expandDatabaseName(dbName, target, nullptr); setup(); @@ -746,7 +778,7 @@ public: { if (!(sMem->process[process].flags & MappingHeader::FLAG_ACTIVE)) break; - if (!ISC_check_process_existence(processId)) + if (!ISC_check_process_existence(sMem->process[process].id)) { sharedMemory->eventFini(&sMem->process[process].notifyEvent); sharedMemory->eventFini(&sMem->process[process].callbackEvent); @@ -832,7 +864,7 @@ private: } catch (const Exception& ex) { - exceptionHandler(ex, NULL); + exceptionHandler(ex, nullptr); } } @@ -907,70 +939,6 @@ void setupIpc() mappingIpc->setup(); } -class DbHandle : public AutoPtr > -{ -public: - DbHandle() - : AutoPtr() - { } - - DbHandle(IAttachment* att) - : AutoPtr(att) - { - if (att) - { - MAP_DEBUG(fprintf(stderr, "Using existing db handle %p\n", att)); - att->addRef(); - } - } - - bool attach(FbLocalStatus& st, const char* aliasDb, ICryptKeyCallback* cryptCb) - { - bool down = false; // true if on attach db is shutdown - - if (hasData()) - { - MAP_DEBUG(fprintf(stderr, "Already attached %s\n", aliasDb)); - return down; - } - - DispatcherPtr prov; - if (cryptCb) - { - prov->setDbCryptCallback(&st, cryptCb); - check("IProvider::setDbCryptCallback", &st); - } - - ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, 1024, isc_dpb_version1); - embeddedSysdba.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); - embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); - embeddedSysdba.insertByte(isc_dpb_map_attach, TRUE); - embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); - - MAP_DEBUG(fprintf(stderr, "Attach %s\n", aliasDb)); - IAttachment* att = prov->attachDatabase(&st, aliasDb, - embeddedSysdba.getBufferLength(), embeddedSysdba.getBuffer()); - - if (st->getState() & IStatus::STATE_ERRORS) - { - const ISC_STATUS* s = st->getErrors(); - MAP_DEBUG(isc_print_status(s)); - bool missing = fb_utils::containsErrorCode(s, isc_io_error); - down = fb_utils::containsErrorCode(s, isc_shutdown); - if (!(missing || down)) - check("IProvider::attachDatabase", &st); - - // down/missing security DB is not a reason to fail mapping - } - else - reset(att); - MAP_DEBUG(fprintf(stderr, "Att=%p\n", att)); - - return down; - } -}; - - const char* roleSql = "with recursive role_tree as ( " " select rdb$role_name as nm from rdb$roles " @@ -1008,14 +976,14 @@ public: return &sync; } - bool getPrivileges(const PathName& db, const string& name, const string& sql_role, + bool getPrivileges(const PathName& db, const string& name, const string* sqlRole, const string& trusted_role, UserId::Privileges& system_privileges) { DbCache* c; - return databases.get(db, c) && c->getPrivileges(name, sql_role, trusted_role, system_privileges); + return databases.get(db, c) && c->getPrivileges(name, sqlRole, trusted_role, system_privileges); } - void populate(const PathName& db, DbHandle& iDb, const string& name, const string& sql_role, + void populate(const PathName& db, Mapping::DbHandle& iDb, const string& name, const string* sqlRole, const string& trusted_role) { DbCache* c; @@ -1024,7 +992,7 @@ public: c = FB_NEW_POOL(getPool()) DbCache(getPool()); *(databases.put(db)) = c; } - c->populate(iDb, name, sql_role, trusted_role); + c->populate(iDb, name, sqlRole, trusted_role); setupIpc(); } @@ -1046,7 +1014,7 @@ private: pairs(p) { } - bool getPrivileges(const string& name, const string& sql_role, const string& trusted_role, + bool getPrivileges(const string& name, const string* sqlRole, const string& trusted_role, UserId::Privileges& system_privileges) { system_privileges.clearAll(); @@ -1056,20 +1024,21 @@ private: MAP_DEBUG(fprintf(stderr, "name=%s\n", name.c_str())); bool granted = false; - if (!pairs.isRoleGranted(name, sql_role, granted)) + if (!pairs.isRoleGranted(name, sqlRole, granted)) return false; MAP_DEBUG(fprintf(stderr, "granted=%d\n", granted)); - return roles.getPrivileges((granted ? sql_role : trusted_role), system_privileges); + return roles.getPrivileges((granted ? *sqlRole : trusted_role), system_privileges); } - void populate(DbHandle& iDb, const string& name, const string& sql_role, + void populate(Mapping::DbHandle& iDb, const string& name, const string* sqlRole, const string& trusted_role) { logins.populate(name, iDb); roles.populate(trusted_role, iDb); - roles.populate(sql_role, iDb); - pairs.populate(name, sql_role, iDb); + if (sqlRole) + roles.populate(*sqlRole, iDb); + pairs.populate(name, sqlRole, iDb); } void invalidate() @@ -1101,7 +1070,7 @@ private: return true; } - void populate(const string& key, DbHandle& iDb) + void populate(const string& key, Mapping::DbHandle& iDb) { if (!key.hasData()) return; @@ -1110,14 +1079,14 @@ private: return; ThrowLocalStatus st; - RefPtr tra(REF_NO_INCR, iDb->startTransaction(&st, 0, NULL)); + RefPtr tra(REF_NO_INCR, iDb->startTransaction(&st, 0, nullptr)); Message par; Field user(par, MAX_SQL_IDENTIFIER_SIZE); user = key.c_str(); RefPtr curs(REF_NO_INCR, iDb->openCursor(&st, tra, 0, sql, 3, - par.getMetadata(), par.getBuffer(), NULL, NULL, 0)); + par.getMetadata(), par.getBuffer(), nullptr, nullptr, 0)); RefPtr meta(curs->getMetadata(&st)); AutoPtr > buffer(FB_NEW UCHAR[meta->getMessageLength(&st)]); @@ -1153,9 +1122,9 @@ private: : GenericMap(p) { } - bool isRoleGranted(const string& name, const string& role, bool& granted) + bool isRoleGranted(const string& name, const string* role, bool& granted) { - if (!(name.hasData() && role.hasData())) + if (!(name.hasData() && role)) { granted = false; return true; @@ -1167,23 +1136,23 @@ private: string zRole; zRole += ROLESEP; - zRole += role; + zRole += *role; zRole += ROLESEP; - MAP_DEBUG(fprintf(stderr, "isRoleGranted '%s' '%s'\n", r->c_str(), role.c_str())); + MAP_DEBUG(fprintf(stderr, "isRoleGranted '%s' '%s'\n", r->c_str(), role->c_str())); granted = r->find(zRole) != string::npos; return true; } - void populate(const string& name, const string& /*role*/, DbHandle& iDb) + void populate(const string& name, const string* /*role*/, Mapping::DbHandle& iDb) { MAP_DEBUG(fprintf(stderr, "populate %s\n", name.c_str())); if (!name.hasData()) return; ThrowLocalStatus st; - RefPtr tra(REF_NO_INCR, iDb->startTransaction(&st, 0, NULL)); + RefPtr tra(REF_NO_INCR, iDb->startTransaction(&st, 0, nullptr)); Message par; Field user(par, MAX_SQL_IDENTIFIER_SIZE); @@ -1196,7 +1165,7 @@ private: "where RDB$USER = ? and RDB$PRIVILEGE = 'M' and RDB$USER_TYPE = 8 and RDB$OBJECT_TYPE = 13"; RefPtr curs(REF_NO_INCR, iDb->openCursor(&st, tra, 0, sql, 3, - par.getMetadata(), par.getBuffer(), cols.getMetadata(), NULL, 0)); + par.getMetadata(), par.getBuffer(), cols.getMetadata(), nullptr, 0)); void* buffer = cols.getBuffer(); string z; @@ -1234,11 +1203,11 @@ void resetMap(const char* db, ULONG index) { switch(index) { - case MAPPING_CACHE: + case Mapping::MAPPING_CACHE: resetMap(db); break; - case SYSTEM_PRIVILEGES_CACHE: + case Mapping::SYSTEM_PRIVILEGES_CACHE: spCache().invalidate(db); break; @@ -1252,177 +1221,249 @@ void resetMap(const char* db, ULONG index) namespace Jrd { -ULONG mapUser(const bool throwNotFoundError, - string& name, string& trusted_role, Firebird::string* auth_method, - AuthReader::AuthBlock* newAuthBlock, UserId::Privileges* system_privileges, - const AuthReader::AuthBlock& authBlock, const char* alias, const char* db, - const char* securityAlias, const string& sql_role, - ICryptKeyCallback* cryptCb, Firebird::IAttachment* att) +Mapping::Mapping(const ULONG f, Firebird::ICryptKeyCallback* cryptCb) + : flags(f), + internalFlags(0), + cryptCallback(cryptCb), + authMethod(nullptr), + newAuthBlock(nullptr), + systemPrivileges(nullptr), + authBlock(nullptr), + mainAlias(nullptr), + mainDb(nullptr), + securityAlias(nullptr), + errorMessagesContext(nullptr), + sqlRole(nullptr) +{ } + +bool Mapping::ensureCachePresence(RefPtr& cache, const char* alias, const char* target, + Mapping::DbHandle& hdb, ICryptKeyCallback* cryptCb, Mapping::Cache* c2) +{ + fb_assert(!cache); + fb_assert(authBlock); + + if (!(authBlock && authBlock->hasData())) + return false; + + MutexEnsureUnlock g(treeMutex, FB_FUNCTION); + g.enter(); + + // Find cache in the tree or create new one + locate(cache, alias, target); + fb_assert(cache); + + // If we use self security database no sense performing checks on it twice + if (cache == c2) + { + cache = nullptr; + return false; + } + + // Required cache(s) are locked somehow - release treeMutex + g.leave(); + + // Std safe check for data presence in cache + if (cache->dataFlag) + return false; + MutexLockGuard g2(cache->populateMutex, FB_FUNCTION); + if (cache->dataFlag) + return false; + + // Create db attachment if missing it and populate cache from it + bool down = hdb.attach(alias, cryptCb) || cache->populate(hdb); + if (down) + cache = nullptr; + return down; +} + + +void Mapping::needAuthMethod(Firebird::string& method) +{ + fb_assert(!authMethod); + authMethod = &method; +} + +void Mapping::needAuthBlock(Firebird::AuthReader::AuthBlock& block) +{ + fb_assert(!newAuthBlock); + newAuthBlock = █ +} + +void Mapping::needSystemPrivileges(UserId::Privileges& privileges) +{ + fb_assert(!systemPrivileges); + systemPrivileges = &privileges; +} + +void Mapping::setAuthBlock(const Firebird::AuthReader::AuthBlock& block) +{ + fb_assert(!authBlock); + authBlock = █ +} + +void Mapping::setInternalFlags() +{ + internalFlags &= ~(FLAG_DB | FLAG_SEC); + + if (!mainDb) + internalFlags |= FLAG_DB; + if (!securityAlias) + internalFlags |= FLAG_SEC; + + // detect presence of this databases mapping in authBlock + // in that case mapUser was already invoked for it + if (authBlock) + { + AuthReader::Info info; + for (AuthReader rdr(*authBlock); rdr.getInfo(info); rdr.moveNext()) + { + if (mainDb && info.secDb == mainDb) + internalFlags |= FLAG_DB; + if (securityAlias && info.secDb == secExpanded.c_str()) + internalFlags |= FLAG_SEC; + } + } +} + +void Mapping::setSqlRole(const Firebird::string& role) +{ + fb_assert(!sqlRole); + sqlRole = &role; +} + +void Mapping::setDb(const char* a, const char* d, Firebird::IAttachment* attachment) +{ + fb_assert(!mainAlias); + fb_assert(!mainDb); + fb_assert(!mainHandle); + fb_assert(authBlock); + + mainAlias = a; + mainDb = d; + mainHandle.setAttachment(attachment); + setInternalFlags(); + + if ((!(internalFlags & FLAG_DB)) && + ensureCachePresence(dbCache, mainAlias, mainDb, mainHandle, cryptCallback, secCache)) + { + internalFlags |= FLAG_DOWN_DB; + } +} + +void Mapping::clearMainHandle() +{ + mainHandle.clear(); +} + +Mapping::~Mapping() +{ + MAP_DEBUG(if (mainHandle) {mainHandle->addRef(); int r = mainHandle->release();) + MAP_DEBUG(fprintf(stderr, "~M:Drop MH with refcount %d\n", r);} else fprintf(stderr, "~M:No MH\n");) +} + +void Mapping::setSecurityDbAlias(const char* a, const char* mainExpandedName) +{ + fb_assert(!securityAlias); + fb_assert(authBlock); + + securityAlias = a; + expandDatabaseName(securityAlias, secExpanded, nullptr); + setInternalFlags(); + + if (mainExpandedName && secExpanded == mainExpandedName) + return; + + Mapping::DbHandle secHandle; + if ((!(internalFlags & FLAG_SEC)) && + ensureCachePresence(secCache, securityAlias, secExpanded.c_str(), secHandle, cryptCallback, dbCache)) + { + internalFlags |= FLAG_DOWN_SEC; + } +} + +void Mapping::setErrorMessagesContextName(const char* context) +{ + errorMessagesContext = context; +} + + +ULONG Mapping::mapUser(string& name, string& trustedRole) { AuthReader::Info info; - if (!securityAlias) + if (flags & MAP_ERROR_HANDLER) { // We are in the error handler - perform minimum processing - trusted_role = ""; + trustedRole = ""; name = ""; - for (AuthReader rdr(authBlock); rdr.getInfo(info); rdr.moveNext()) + if (authBlock) { - if (info.type == NM_USER && info.name.hasData()) + for (AuthReader rdr(*authBlock); rdr.getInfo(info); rdr.moveNext()) { - name = info.name.ToString(); - break; + if (info.type == NM_USER && info.name.hasData()) + { + name = info.name.ToString(); + break; + } } } return 0; } - // expand security database name (db is expected to be expanded, alias - original) - PathName secExpanded; - expandDatabaseName(securityAlias, secExpanded, NULL); - const char* securityDb = secExpanded.c_str(); - bool secDown = false; - bool dbDown = false; - DbHandle iDb(att); - FbLocalStatus st; - // Create new writer AuthWriter newBlock; - // detect presence of this databases mapping in authBlock - // in that case mapUser was already invoked for it - unsigned flags = db ? 0 : FLAG_DB; - for (AuthReader rdr(authBlock); rdr.getInfo(info); rdr.moveNext()) - { - if (db && info.secDb == db) - flags |= FLAG_DB; - if (info.secDb == securityDb) - flags |= FLAG_SEC; - } - - // Perform lock & map only when needed - if ((flags != (FLAG_DB | FLAG_SEC)) && authBlock.hasData()) + // Map it only when needed + if (authBlock && authBlock->hasData() && (dbCache || secCache)) { AuthReader::Info info; - SyncType syncType = SYNC_SHARED; - DbHandle iSec; - for (;;) + // Caches are ready somehow - proceed with analysis + AuthReader auth(*authBlock); + + // Map in simple mode first main, next security db + if (!(dbCache && dbCache->map4(false, internalFlags & FLAG_DB, auth, info, newBlock))) { - if (syncType == SYNC_EXCLUSIVE) + if (!(secCache && secCache->map4(false, internalFlags & FLAG_SEC, auth, info, newBlock))) { - if (!iSec) - iSec.attach(st, securityAlias, cryptCb); - - if (db && !iDb) - iDb.attach(st, alias, cryptCb); - } - - MutexEnsureUnlock g(treeMutex, FB_FUNCTION); - g.enter(); - - Cache* cDb = NULL; - if (db) - cDb = locate(alias, db); - Cache* cSec = locate(securityAlias, securityDb); - if (cDb == cSec) - cDb = NULL; - - SyncObject dummySync1, dummySync2; - Sync sDb(((!(flags & FLAG_DB)) && cDb) ? &cDb->syncObject : &dummySync1, FB_FUNCTION); - Sync sSec((!(flags & FLAG_SEC)) ? &cSec->syncObject : &dummySync2, FB_FUNCTION); - - sSec.lock(syncType); - if (!sDb.lockConditional(syncType)) - { - // Avoid deadlocks cause hell knows which db is security for which - sSec.unlock(); - // Now safely wait for sSec - sDb.lock(syncType); - // and repeat whole operation - continue; - } - - // Required cache(s) are locked somehow - release treeMutex - g.leave(); - - // Check is it required to populate caches from DB - if ((cDb && !cDb->dataFlag) || !cSec->dataFlag) - { - if (syncType != SYNC_EXCLUSIVE) + // Map in wildcard mode first main, next security db + if (!(dbCache && dbCache->map4(true, internalFlags & FLAG_DB, auth, info, newBlock))) { - syncType = SYNC_EXCLUSIVE; - sSec.unlock(); - sDb.unlock(); - - continue; + if (secCache) + secCache->map4(true, internalFlags & FLAG_SEC, auth, info, newBlock); } - - if (cDb) - { - MAP_DEBUG(fprintf(stderr, "Populate cache for main DB %p\n", iDb.get())); - cDb->populate(iDb, dbDown); - } - - MAP_DEBUG(fprintf(stderr, "Populate cache for sec DB %p\n", iSec.get())); - - cSec->populate(iSec, secDown); - - sSec.downgrade(SYNC_SHARED); - sDb.downgrade(SYNC_SHARED); } - - // use down flags from caches - if (cDb) - dbDown = cDb->downFlag; - secDown = cSec->downFlag; - - // Caches are ready somehow - proceed with analysis - AuthReader auth(authBlock); - - // Map in simple mode first main, next security db - if (cDb && cDb->map4(false, flags & FLAG_DB, auth, info, newBlock)) - break; - if (cSec->map4(false, flags & FLAG_SEC, auth, info, newBlock)) - break; - - // Map in wildcard mode first main, next security db - if (cDb && cDb->map4(true, flags & FLAG_DB, auth, info, newBlock)) - break; - cSec->map4(true, flags & FLAG_SEC, auth, info, newBlock); - - break; - } - - for (AuthReader rdr(newBlock); rdr.getInfo(info); rdr.moveNext()) - { - if (db && info.secDb == db) - flags |= FLAG_DB; - if (info.secDb == securityDb) - flags |= FLAG_SEC; - } - - // mark both DBs as 'seen' - info.plugin = ""; - info.name = ""; - info.type = TYPE_SEEN; - - if (!(flags & FLAG_DB)) - { - info.secDb = db; - newBlock.add(info); - } - - if (!(flags & FLAG_SEC)) - { - info.secDb = securityDb; - newBlock.add(info); } } - newBlock.append(authBlock); + for (AuthReader rdr(newBlock); rdr.getInfo(info); rdr.moveNext()) + { + if (mainDb && info.secDb == mainDb) + internalFlags |= FLAG_DB; + if (info.secDb == secExpanded.c_str()) + internalFlags |= FLAG_SEC; + } + + // mark both DBs as 'seen' + info.plugin = ""; + info.name = ""; + info.type = TYPE_SEEN; + + if (!(internalFlags & FLAG_DB)) + { + info.secDb = mainDb; + newBlock.add(info); + } + + if (!(internalFlags & FLAG_SEC)) + { + info.secDb = secExpanded.c_str(); + newBlock.add(info); + } + + newBlock.append(*authBlock); Found fName, fRole; MAP_DEBUG(fprintf(stderr, "Starting newblock scan\n")); @@ -1432,8 +1473,8 @@ ULONG mapUser(const bool throwNotFoundError, info.secDb.c_str(), info.plugin.c_str(), info.type.c_str(), info.name.c_str(), info.origPlug.c_str())); Found::What recordWeight = - (db && info.secDb == db) ? Found::FND_DB : - (info.secDb == securityDb) ? Found::FND_SEC : + (mainDb && info.secDb == mainDb) ? Found::FND_DB : + (securityAlias && info.secDb == secExpanded.c_str()) ? Found::FND_SEC : Found::FND_NOTHING; if (recordWeight != Found::FND_NOTHING) @@ -1445,26 +1486,26 @@ ULONG mapUser(const bool throwNotFoundError, } } - ULONG rc = secDown || dbDown ? MAPUSER_MAP_DOWN : 0; + ULONG rc = (internalFlags & (FLAG_DOWN_DB | FLAG_DOWN_SEC)) ? MAP_DOWN : 0; if (fName.found == Found::FND_NOTHING) { - if (throwNotFoundError) + if (flags & MAP_THROW_NOT_FOUND) { Arg::Gds v(isc_sec_context); - v << alias; - if (rc & MAPUSER_MAP_DOWN) + v << (errorMessagesContext ? errorMessagesContext : mainAlias ? mainAlias : ""); + if (rc & MAP_DOWN) v << Arg::Gds(isc_map_down); v.raise(); } - rc |= MAPUSER_ERROR_NOT_THROWN; + rc |= MAP_ERROR_NOT_THROWN; } else { name = fName.value.ToString(); - trusted_role = fRole.value.ToString(); - MAP_DEBUG(fprintf(stderr, "login=%s tr=%s\n", name.c_str(), trusted_role.c_str())); - if (auth_method) - *auth_method = fName.method.ToString(); + trustedRole = fRole.value.ToString(); + MAP_DEBUG(fprintf(stderr, "login=%s tr=%s\n", name.c_str(), trustedRole.c_str())); + if (authMethod) + *authMethod = fName.method.ToString(); if (newAuthBlock) { @@ -1475,32 +1516,31 @@ ULONG mapUser(const bool throwNotFoundError, } } - if (name.hasData() || trusted_role.hasData()) + if (name.hasData() || trustedRole.hasData()) { - if (system_privileges && db) + if (systemPrivileges && mainDb) { - system_privileges->clearAll(); + systemPrivileges->clearAll(); Sync sync(spCache().getSync(), FB_FUNCTION); sync.lock(SYNC_SHARED); MAP_DEBUG(fprintf(stderr, "GP: name=%s sql=%s trusted=%s\n", - name.c_str(), sql_role.c_str(), trusted_role.c_str())); + name.c_str(), sqlRole ? sqlRole->c_str() : "", trustedRole.c_str())); - if (!spCache().getPrivileges(db, name, sql_role, trusted_role, *system_privileges)) + if (!spCache().getPrivileges(mainDb, name, sqlRole, trustedRole, *systemPrivileges)) { sync.unlock(); sync.lock(SYNC_EXCLUSIVE); - if (!spCache().getPrivileges(db, name, sql_role, trusted_role, *system_privileges)) + if (!spCache().getPrivileges(mainDb, name, sqlRole, trustedRole, *systemPrivileges)) { - if (!iDb) - iDb.attach(st, alias, cryptCb); + mainHandle.attach(mainAlias, cryptCallback); - if (iDb) + if (mainHandle) { - spCache().populate(db, iDb, name, sql_role, trusted_role); - spCache().getPrivileges(db, name, sql_role, trusted_role, *system_privileges); + spCache().populate(mainDb, mainHandle, name, sqlRole, trustedRole); + spCache().getPrivileges(mainDb, name, sqlRole, trustedRole, *systemPrivileges); } } } @@ -1510,11 +1550,12 @@ ULONG mapUser(const bool throwNotFoundError, return rc; } -void clearMappingCache(const char* dbName, USHORT index) +void Mapping::clearCache(const char* dbName, USHORT index) { mappingIpc->clearCache(dbName, index); } + const Format* GlobalMappingScan::getFormat(thread_db* tdbb, jrd_rel* relation) const { jrd_tra* const transaction = tdbb->getTransaction(); @@ -1552,17 +1593,16 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) FbLocalStatus st; DispatcherPtr prov; - IAttachment* att = NULL; - ITransaction* tra = NULL; - IResultSet* curs = NULL; + IAttachment* att = nullptr; + ITransaction* tra = nullptr; + IResultSet* curs = nullptr; try { - ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, - MAX_DPB_SIZE, isc_dpb_version1); - embeddedSysdba.insertString(isc_dpb_user_name, DBA_USER_NAME, - fb_strlen(DBA_USER_NAME)); + ClumpletWriter embeddedSysdba(ClumpletWriter::Tagged, MAX_DPB_SIZE, isc_dpb_version1); + embeddedSysdba.insertString(isc_dpb_user_name, DBA_USER_NAME, fb_strlen(DBA_USER_NAME)); embeddedSysdba.insertByte(isc_dpb_sec_attach, TRUE); + embeddedSysdba.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); embeddedSysdba.insertByte(isc_dpb_no_db_triggers, TRUE); const char* dbName = tdbb->getDatabase()->dbb_config->getSecurityDatabase(); @@ -1600,7 +1640,7 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) "SELECT RDB$MAP_NAME, RDB$MAP_USING, RDB$MAP_PLUGIN, RDB$MAP_DB, " " RDB$MAP_FROM_TYPE, RDB$MAP_FROM, RDB$MAP_TO_TYPE, RDB$MAP_TO " "FROM RDB$AUTH_MAPPING", - 3, NULL, NULL, mMap.getMetadata(), NULL, 0); + 3, nullptr, nullptr, mMap.getMetadata(), nullptr, 0); if (st->getState() & IStatus::STATE_ERRORS) { if (!fb_utils::containsErrorCode(st->getErrors(), isc_dsql_relation_err)) @@ -1674,15 +1714,15 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) curs->close(&st); check("IResultSet::close", &st); - curs = NULL; + curs = nullptr; tra->rollback(&st); check("ITransaction::rollback", &st); - tra = NULL; + tra = nullptr; att->detach(&st); check("IAttachment::detach", &st); - att = NULL; + att = nullptr; } catch (const Exception&) { @@ -1700,7 +1740,7 @@ RecordBuffer* MappingList::getList(thread_db* tdbb, jrd_rel* relation) return getData(relation); } -void shutdownMappingIpc() +void Mapping::shutdownIpc() { mappingIpc->shutdown(); } diff --git a/src/jrd/Mapping.h b/src/jrd/Mapping.h index 1838da6ba9..7c3f2aeca1 100644 --- a/src/jrd/Mapping.h +++ b/src/jrd/Mapping.h @@ -31,29 +31,137 @@ #include "../common/classes/alloc.h" #include "../common/classes/fb_string.h" -#include "../common/classes/ClumpletReader.h" +#include "../common/classes/ClumpletWriter.h" +#include "../common/classes/Hash.h" +#include "../common/classes/GenericMap.h" #include "../jrd/recsrc/RecordSource.h" #include "../jrd/Monitoring.h" #include "../jrd/scl.h" namespace Jrd { -ULONG mapUser(const bool throwNotFoundError, - Firebird::string& name, Firebird::string& trusted_role, Firebird::string* auth_method, - Firebird::AuthReader::AuthBlock* newAuthBlock, UserId::Privileges* system_privileges, - const Firebird::AuthReader::AuthBlock& authBlock, const char* alias, const char* db, - const char* securityDb, const Firebird::string& sql_role, - Firebird::ICryptKeyCallback* cryptCb, Firebird::IAttachment* att); -// bits returned by mapUser -const ULONG MAPUSER_ERROR_NOT_THROWN = 1; -const ULONG MAPUSER_MAP_DOWN = 2; +class AuthWriter; -void clearMappingCache(const char* dbName, USHORT index); -// possible index values -const USHORT MAPPING_CACHE = 0; -const USHORT SYSTEM_PRIVILEGES_CACHE = 1; +class Mapping +{ +public: + // constructor's flags + static const ULONG MAP_NO_FLAGS = 0; + static const ULONG MAP_THROW_NOT_FOUND = 1; + static const ULONG MAP_ERROR_HANDLER = 2; + Mapping(const ULONG flags, Firebird::ICryptKeyCallback* cryptCb); -void shutdownMappingIpc(); + ~Mapping(); + + // First provide the main input information - old auth block ... + void setAuthBlock(const Firebird::AuthReader::AuthBlock& authBlock); + + // ... and finally specify additional information when available. + void setSqlRole(const Firebird::string& sqlRole); + void setDb(const char* alias, const char* db, Firebird::IAttachment* att); + void setSecurityDbAlias(const char* alias, const char* mainExpandedName); + void setErrorMessagesContextName(const char* context); + + // This should be done before mapUser(). + void needAuthMethod(Firebird::string& authMethod); + void needAuthBlock(Firebird::AuthReader::AuthBlock& newAuthBlock); + void needSystemPrivileges(UserId::Privileges& systemPrivileges); + + // bits returned by mapUser + static const ULONG MAP_ERROR_NOT_THROWN = 1; + static const ULONG MAP_DOWN = 2; + // Now mapper is ready to perform main task and provide mapped login and trusted role. + ULONG mapUser(Firebird::string& name, Firebird::string& trustedRole); + + // Do not keep mainHandle opened longer than needed + void clearMainHandle(); + + // possible clearCache() flags + static const USHORT MAPPING_CACHE = 0; + static const USHORT SYSTEM_PRIVILEGES_CACHE = 1; + // Helper statuc functions to perform cleanup & shutdown. + static void clearCache(const char* dbName, USHORT id); + static void shutdownIpc(); + +private: + const ULONG flags; + ULONG internalFlags; + Firebird::ICryptKeyCallback* cryptCallback; + Firebird::string* authMethod; + Firebird::AuthReader::AuthBlock* newAuthBlock; + UserId::Privileges* systemPrivileges; + const Firebird::AuthReader::AuthBlock* authBlock; + const char* mainAlias; + const char* mainDb; + const char* securityAlias; + const char* errorMessagesContext; + const Firebird::string* sqlRole; + +public: + class DbHandle : public Firebird::RefPtr + { + public: + DbHandle(); + void setAttachment(Firebird::IAttachment* att); + void clear(); + bool attach(const char* aliasDb, Firebird::ICryptKeyCallback* cryptCb); + }; + + class Map; + typedef Firebird::HashTable, Map> MapHash; + + class Map : public MapHash::Entry, public Firebird::GlobalStorage + { + public: + Map(const char* aUsing, const char* aPlugin, const char* aDb, + const char* aFromType, const char* aFrom, + SSHORT aRole, const char* aTo); + explicit Map(Firebird::AuthReader::Info& info); //type, name, plugin, secDb + + static FB_SIZE_T hash(const Map& value, FB_SIZE_T hashSize); + Firebird::NoCaseString makeHashKey() const; + void trimAll(); + virtual bool isEqual(const Map& k) const; + virtual Map* get(); + + Firebird::NoCaseString plugin, db, fromType, from, to; + bool toRole; + char usng; + }; + + class Cache : public MapHash, public Firebird::GlobalStorage, public Firebird::RefCounted + { + public: + Cache(const Firebird::NoCaseString& aliasDb, const Firebird::NoCaseString& db); + ~Cache(); + + bool populate(Firebird::IAttachment *att); + void map(bool flagWild, Firebird::AuthReader::Info& info, AuthWriter& newBlock); + void search(Firebird::AuthReader::Info& info, const Map& from, AuthWriter& newBlock, + const Firebird::NoCaseString& originalUserName); + void varPlugin(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock); + void varDb(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock); + void varFrom(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock); + void varUsing(Firebird::AuthReader::Info& info, Map from, AuthWriter& newBlock); + bool map4(bool flagWild, unsigned flagSet, Firebird::AuthReader& rdr, + Firebird::AuthReader::Info& info, AuthWriter& newBlock); + static void eraseEntry(Map* m); + + public: + Firebird::Mutex populateMutex; + Firebird::NoCaseString alias, name; + bool dataFlag; + }; + +private: + Firebird::PathName secExpanded; + Firebird::RefPtr dbCache, secCache; + DbHandle mainHandle; + + void setInternalFlags(); + bool ensureCachePresence(Firebird::RefPtr& cache, const char* alias, + const char* target, DbHandle& hdb, Firebird::ICryptKeyCallback* cryptCb, Cache* c2); +}; class GlobalMappingScan: public VirtualTableScan { diff --git a/src/jrd/Optimizer.cpp b/src/jrd/Optimizer.cpp index 149932e1c2..4943fb387a 100644 --- a/src/jrd/Optimizer.cpp +++ b/src/jrd/Optimizer.cpp @@ -70,8 +70,8 @@ bool checkExpressionIndex(const index_desc* idx, ValueExprNode* node, StreamType // so try to recover it (see CORE-4118). while (!idx->idx_expression->sameAs(node, true)) { - DerivedExprNode* const derivedExpr = node->as(); - CastNode* const cast = node->as(); + DerivedExprNode* const derivedExpr = nodeAs(node); + CastNode* const cast = nodeAs(node); if (derivedExpr) node = derivedExpr->arg; @@ -490,7 +490,7 @@ InversionCandidate* OptimizerRetrieval::generateInversion() for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) { BoolExprNode* const node = tail->opt_conjunct_node; - BinaryBoolNode* booleanNode = node->as(); + BinaryBoolNode* booleanNode = nodeAs(node); if (!(tail->opt_conjunct_flags & opt_conjunct_used) && node && (!booleanNode || booleanNode->blrOp != blr_or)) @@ -505,7 +505,7 @@ InversionCandidate* OptimizerRetrieval::generateInversion() for (OptimizerBlk::opt_conjunct* tail = opt_begin; tail < opt_end; tail++) { BoolExprNode* const node = tail->opt_conjunct_node; - BinaryBoolNode* booleanNode = node->as(); + BinaryBoolNode* booleanNode = nodeAs(node); if (!(tail->opt_conjunct_flags & opt_conjunct_used) && node && (booleanNode && booleanNode->blrOp == blr_or)) @@ -566,7 +566,7 @@ InversionCandidate* OptimizerRetrieval::generateInversion() node->computable(csb, stream, true) && !invCandidate->matches.exist(node)) { - const ComparativeBoolNode* const cmpNode = node->as(); + const ComparativeBoolNode* const cmpNode = nodeAs(node); const double factor = (cmpNode && cmpNode->blrOp == blr_eql) ? REDUCE_SELECTIVITY_FACTOR_EQUALITY : REDUCE_SELECTIVITY_FACTOR_INEQUALITY; @@ -716,7 +716,7 @@ void OptimizerRetrieval::analyzeNavigation() break; } } - else if (!(fieldNode = node->as()) || fieldNode->fieldStream != stream) + else if (!(fieldNode = nodeAs(node)) || fieldNode->fieldStream != stream) { usableIndex = false; break; @@ -1029,7 +1029,7 @@ ValueExprNode* OptimizerRetrieval::findDbKey(ValueExprNode* dbkey, SLONG* positi * **************************************/ - const RecordKeyNode* keyNode = dbkey->as(); + const RecordKeyNode* keyNode = nodeAs(dbkey); if (keyNode && keyNode->blrOp == blr_dbkey) { @@ -1040,7 +1040,7 @@ ValueExprNode* OptimizerRetrieval::findDbKey(ValueExprNode* dbkey, SLONG* positi return NULL; } - ConcatenateNode* concatNode = dbkey->as(); + ConcatenateNode* concatNode = nodeAs(dbkey); if (concatNode) { @@ -1652,10 +1652,10 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) return false; - ComparativeBoolNode* cmpNode = boolean->as(); - MissingBoolNode* missingNode = boolean->as(); - NotBoolNode* notNode = boolean->as(); - RseBoolNode* rseNode = boolean->as(); + ComparativeBoolNode* cmpNode = nodeAs(boolean); + MissingBoolNode* missingNode = nodeAs(boolean); + NotBoolNode* notNode = nodeAs(boolean); + RseBoolNode* rseNode = nodeAs(boolean); bool forward = true; ValueExprNode* value = NULL; ValueExprNode* match = NULL; @@ -1707,7 +1707,7 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* FieldNode* fieldNode; - if (!(fieldNode = match->as()) || + if (!(fieldNode = nodeAs(match)) || fieldNode->fieldStream != stream || (value && !value->computable(csb, stream, false))) { @@ -1715,7 +1715,7 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* match = value; value = temp; - if ((!match || !(fieldNode = match->as())) || + if ((!match || !(fieldNode = nodeAs(match))) || fieldNode->fieldStream != stream || !value->computable(csb, stream, false)) { @@ -1778,7 +1778,7 @@ bool OptimizerRetrieval::matchBoolean(IndexScratch* indexScratch, BoolExprNode* for (int i = 0; i < indexScratch->idx->idx_count; i++) { - FieldNode* fieldNode = match->as(); + FieldNode* fieldNode = nodeAs(match); if (!(indexScratch->idx->idx_flags & idx_expressn)) { @@ -1996,7 +1996,7 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const **************************************/ // If this isn't an equality, it isn't even interesting - ComparativeBoolNode* cmpNode = boolean->as(); + ComparativeBoolNode* cmpNode = nodeAs(boolean); if (!cmpNode || cmpNode->blrOp != blr_eql) return NULL; @@ -2007,15 +2007,15 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const ValueExprNode* dbkey = cmpNode->arg1; ValueExprNode* value = cmpNode->arg2; - const RecordKeyNode* keyNode = dbkey->as(); + const RecordKeyNode* keyNode = nodeAs(dbkey); if (!(keyNode && keyNode->blrOp == blr_dbkey && keyNode->recStream == stream) && - !dbkey->is()) + !nodeIs(dbkey)) { - keyNode = value->as(); + keyNode = nodeAs(value); if (!(keyNode && keyNode->blrOp == blr_dbkey && keyNode->recStream == stream) && - !value->is()) + !nodeIs(value)) { return NULL; } @@ -2032,7 +2032,7 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const // If this is a concatenation, find an appropriate dbkey SLONG n = 0; - if (dbkey->is()) + if (nodeIs(dbkey)) { dbkey = findDbKey(dbkey, &n); if (!dbkey) @@ -2041,7 +2041,7 @@ InversionCandidate* OptimizerRetrieval::matchDbKey(BoolExprNode* boolean) const // Make sure we have the correct stream - keyNode = dbkey->as(); + keyNode = nodeAs(dbkey); if (!keyNode || keyNode->blrOp != blr_dbkey || keyNode->recStream != stream) return NULL; @@ -2083,7 +2083,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( * inversion candidate could be returned. * **************************************/ - BinaryBoolNode* binaryNode = boolean->as(); + BinaryBoolNode* binaryNode = nodeAs(boolean); // Handle the "OR" case up front if (binaryNode && binaryNode->blrOp == blr_or) @@ -2114,7 +2114,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( if (invCandidate1) inversions.add(invCandidate1); - BinaryBoolNode* childBoolNode = binaryNode->arg1->as(); + BinaryBoolNode* childBoolNode = nodeAs(binaryNode->arg1); // Get usable inversions based on indexOrScratches and scope if (!childBoolNode || childBoolNode->blrOp != blr_or) @@ -2144,7 +2144,7 @@ InversionCandidate* OptimizerRetrieval::matchOnIndexes( if (invCandidate2) inversions.add(invCandidate2); - childBoolNode = binaryNode->arg2->as(); + childBoolNode = nodeAs(binaryNode->arg2); // Make inversion based on indexOrScratches and scope if (!childBoolNode || childBoolNode->blrOp != blr_or) @@ -2404,7 +2404,7 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB } else { - FieldNode* fieldNode = field->as(); + FieldNode* fieldNode = nodeAs(field); if (!fieldNode) { @@ -2414,7 +2414,7 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB // this must include many matches (think about empty string) return false; /* - if (!value->is()) + if (!nodeIs(value)) return NULL; field = value; value = cmpNode->arg1; @@ -2422,7 +2422,7 @@ bool OptimizerRetrieval::validateStarts(IndexScratch* indexScratch, ComparativeB } // Every string starts with an empty string so don't bother using an index in that case. - LiteralNode* literal = value->as(); + LiteralNode* literal = nodeAs(value); if (literal) { diff --git a/src/jrd/PreparedStatement.cpp b/src/jrd/PreparedStatement.cpp index 3dfd30697a..2b2f06a918 100644 --- a/src/jrd/PreparedStatement.cpp +++ b/src/jrd/PreparedStatement.cpp @@ -99,6 +99,16 @@ namespace item.length = sizeof(double); break; + case dtype_dec64: + item.type = SQL_DEC16; + item.length = sizeof(Decimal64); + break; + + case dtype_dec128: + item.type = SQL_DEC34; + item.length = sizeof(Decimal128); + break; + case dtype_sql_date: item.type = SQL_TYPE_DATE; item.length = sizeof(SLONG); diff --git a/src/jrd/RecordSourceNodes.cpp b/src/jrd/RecordSourceNodes.cpp index 50bcd79230..9ecb738b8d 100644 --- a/src/jrd/RecordSourceNodes.cpp +++ b/src/jrd/RecordSourceNodes.cpp @@ -561,7 +561,7 @@ RecordSourceNode* RelationSourceNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) bool RelationSourceNode::dsqlMatch(const ExprNode* other, bool /*ignoreMapCast*/) const { - const RelationSourceNode* o = other->as(); + const RelationSourceNode* o = nodeAs(other); return o && dsqlContext == o->dsqlContext; } @@ -1029,7 +1029,7 @@ RecordSourceNode* ProcedureSourceNode::dsqlFieldRemapper(FieldRemapper& visitor) bool ProcedureSourceNode::dsqlMatch(const ExprNode* other, bool /*ignoreMapCast*/) const { - const ProcedureSourceNode* o = other->as(); + const ProcedureSourceNode* o = nodeAs(other); return o && dsqlContext == o->dsqlContext; } @@ -1321,7 +1321,7 @@ RecordSourceNode* AggregateSourceNode::dsqlFieldRemapper(FieldRemapper& visitor) bool AggregateSourceNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const { - const AggregateSourceNode* o = other->as(); + const AggregateSourceNode* o = nodeAs(other); return o && dsqlContext == o->dsqlContext && PASS1_node_match(dsqlGroup, o->dsqlGroup, ignoreMapCast) && @@ -1606,7 +1606,7 @@ RecordSource* AggregateSourceNode::generate(thread_db* tdbb, OptimizerBlk* opt, AggNode* aggNode = NULL; if (map->sourceList.getCount() == 1 && (ptr = map->sourceList.begin()) && - (aggNode = (*ptr)->as()) && + (aggNode = nodeAs(*ptr)) && (aggNode->aggInfo.blr == blr_agg_min || aggNode->aggInfo.blr == blr_agg_max)) { // generate a sort block which the optimizer will try to map to an index @@ -1750,15 +1750,15 @@ void UnionSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch) ValueExprNode* mapItem = dsqlParentRse->dsqlSelectList->items[0]; // AB: First item could be a virtual field generated by derived table. - DerivedFieldNode* derivedField = mapItem->as(); + DerivedFieldNode* derivedField = nodeAs(mapItem); if (derivedField) mapItem = derivedField->value; - if (mapItem->is()) - mapItem = mapItem->as()->source; + if (nodeIs(mapItem)) + mapItem = nodeAs(mapItem)->source; - DsqlMapNode* mapNode = mapItem->as(); + DsqlMapNode* mapNode = nodeAs(mapItem); fb_assert(mapNode); if (!mapNode) @@ -1780,7 +1780,7 @@ void UnionSourceNode::genBlr(DsqlCompilerScratch* dsqlScratch) NestConst* ptr = streams->items.begin(); for (const NestConst* const end = streams->items.end(); ptr != end; ++ptr) { - RseNode* sub_rse = (*ptr)->as(); + RseNode* sub_rse = nodeAs(*ptr); GEN_rse(dsqlScratch, sub_rse); ValueListNode* items = sub_rse->dsqlSelectList; @@ -2506,7 +2506,7 @@ RseNode* RseNode::dsqlFieldRemapper(FieldRemapper& visitor) bool RseNode::dsqlMatch(const ExprNode* other, bool ignoreMapCast) const { - const RseNode* o = other->as(); + const RseNode* o = nodeAs(other); if (!o) return false; @@ -2611,11 +2611,11 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { const TEXT* name = NULL; - if (auto* aliasNode = item->as()) + if (auto* aliasNode = nodeAs(item)) name = aliasNode->name.c_str(); - else if (auto* fieldNode = item->as()) + else if (auto* fieldNode = nodeAs(item)) name = fieldNode->dsqlField->fld_name.c_str(); - else if (auto* derivedField = item->as()) + else if (auto* derivedField = nodeAs(item)) name = derivedField->name.c_str(); if (name) @@ -2650,7 +2650,7 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) for (FB_SIZE_T i = 0; i < usingList->items.getCount(); ++i) { - const FieldNode* field = usingList->items[i]->as(); + const FieldNode* field = nodeAs(usingList->items[i]); // verify if the column was already used FB_SIZE_T pos; @@ -2710,7 +2710,7 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) ValueListNode* stack = FB_NEW_POOL(getPool()) ValueListNode(getPool(), 0u); NestConst tempNode = impJoinLeft->value; - NestConst aliasNode = tempNode->as(); + NestConst aliasNode = nodeAs(tempNode); NestConst coalesceNode; if (aliasNode) @@ -2719,7 +2719,7 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) { // scope PsqlChanger changer(dsqlScratch, false); - if ((coalesceNode = tempNode->as())) + if ((coalesceNode = nodeAs(tempNode))) { ValueListNode* list = coalesceNode->args; @@ -2735,10 +2735,10 @@ RseNode* RseNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) tempNode = impJoinRight->value; - if ((aliasNode = tempNode->as())) + if ((aliasNode = nodeAs(tempNode))) tempNode = aliasNode->value; - if ((coalesceNode = tempNode->as())) + if ((coalesceNode = nodeAs(tempNode))) { ValueListNode* list = coalesceNode->args; @@ -2836,7 +2836,7 @@ RseNode* RseNode::pass1(thread_db* tdbb, CompilerScratch* csb) for (ExprNode** node = csb->csb_current_nodes.begin(); node != csb->csb_current_nodes.end(); ++node) { - if ((*node)->as()) + if (nodeAs(*node)) { topLevelRse = false; break; @@ -3431,8 +3431,8 @@ RseNode* SelectExprNode::dsqlPass(DsqlCompilerScratch* dsqlScratch) static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, RecordSourceNode* source) { - ProcedureSourceNode* procNode = source->as(); - RelationSourceNode* relNode = source->as(); + ProcedureSourceNode* procNode = nodeAs(source); + RelationSourceNode* relNode = nodeAs(source); fb_assert(procNode || relNode); @@ -3483,7 +3483,7 @@ static RecordSourceNode* dsqlPassRelProc(DsqlCompilerScratch* dsqlScratch, Recor } RecordSourceNode* const query = cte->querySpec; - UnionSourceNode* unionQuery = ExprNode::as(query); + UnionSourceNode* unionQuery = nodeAs(query); const bool isRecursive = unionQuery && unionQuery->recursive; const string saveCteName = cte->alias; @@ -3576,7 +3576,7 @@ static void processMap(thread_db* tdbb, CompilerScratch* csb, MapNode* map, Form source != sourceEnd; ++source, ++target) { - FieldNode* field = (*target)->as(); + FieldNode* field = nodeAs(*target); const USHORT id = field->fieldId; if (id >= format->fmt_count) @@ -3677,7 +3677,7 @@ static void genDeliverUnmapped(thread_db* tdbb, BoolExprNodeStack* deliverStack, // Handle the "OR" case first - BinaryBoolNode* const binaryNode = boolean->as(); + BinaryBoolNode* const binaryNode = nodeAs(boolean); if (binaryNode && binaryNode->blrOp == blr_or) { BoolExprNodeStack orgStack, newStack; @@ -3708,8 +3708,8 @@ static void genDeliverUnmapped(thread_db* tdbb, BoolExprNodeStack* deliverStack, // Reduce to simple comparisons - ComparativeBoolNode* const cmpNode = boolean->as(); - MissingBoolNode* const missingNode = boolean->as(); + ComparativeBoolNode* const cmpNode = nodeAs(boolean); + MissingBoolNode* const missingNode = nodeAs(boolean); HalfStaticArray children; if (cmpNode && @@ -3732,7 +3732,7 @@ static void genDeliverUnmapped(thread_db* tdbb, BoolExprNodeStack* deliverStack, for (indexArg = 0; (indexArg < children.getCount()) && !mappingFound; ++indexArg) { - FieldNode* fieldNode = children[indexArg]->as(); + FieldNode* fieldNode = nodeAs(children[indexArg]); if (fieldNode && fieldNode->fieldStream == shellStream) mappingFound = true; @@ -3777,7 +3777,7 @@ static void genDeliverUnmapped(thread_db* tdbb, BoolExprNodeStack* deliverStack, // forget to leave aggregate-functions alone in case of aggregate rse). // Because this is only to help using an index we keep it simple. - FieldNode* fieldNode = children[indexArg]->as(); + FieldNode* fieldNode = nodeAs(children[indexArg]); if (fieldNode && fieldNode->fieldStream == shellStream) { @@ -3827,16 +3827,14 @@ static ValueExprNode* resolveUsingField(DsqlCompilerScratch* dsqlScratch, const FieldNode* fieldNode; DerivedFieldNode* derivedField; - if ((aliasNode = ExprNode::as(node))) + if ((aliasNode = nodeAs(node))) ctx = aliasNode->implicitJoin->visibleInContext; - else if ((fieldNode = ExprNode::as(node))) + else if ((fieldNode = nodeAs(node))) ctx = fieldNode->dsqlContext; - else if ((derivedField = ExprNode::as(node))) + else if ((derivedField = nodeAs(node))) ctx = derivedField->context; else - { fb_assert(false); - } return node; } diff --git a/src/jrd/SysFunction.cpp b/src/jrd/SysFunction.cpp index 3a8e93e36a..a72f23bf66 100644 --- a/src/jrd/SysFunction.cpp +++ b/src/jrd/SysFunction.cpp @@ -76,7 +76,9 @@ enum Function funLPad, funRPad, funLnat, - funLog10 + funLog10, + funTotalOrd, + funCmpDec }; enum TrigonFunction @@ -107,6 +109,8 @@ double fbcot(double value) throw(); // generic setParams functions void setParamsDouble(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); +void setParamsDblDec(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); +void setParamsDecFloat(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsFromList(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsInteger(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); void setParamsSecondInteger(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args); @@ -124,6 +128,8 @@ void setParamsUuidToChar(DataTypeUtilBase* dataTypeUtil, const SysFunction* func // generic make functions void makeDoubleResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeDblDecResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makeDecFloatResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeFromListResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeInt64Result(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeLongResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); @@ -142,6 +148,7 @@ void makeLeftRight(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, void makeMod(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeOverlay(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makePad(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); +void makePi(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeReplace(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeReverse(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); void makeRound(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args); @@ -305,6 +312,77 @@ void setParamsDouble(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** } +template +bool areParamsDouble(int argsCount, DSC** args) +{ + bool decSeen = false; + + for (int i = 0; i < argsCount; ++i) + { + if (args[i]->isApprox()) + return true; + if (args[i]->isDecFloat()) + decSeen = true; + } + + return !decSeen; +} + + +bool areParamsDec64(int argsCount, dsc** args) +{ + bool f64 = false; + + for (int i = 0; i < argsCount; ++i) + { + switch (args[i]->dsc_dtype) + { + case dtype_dec64: + f64 = true; + break; + case dtype_dec128: + return false; + } + } + + return f64; +} + + +void setParamsDblDec(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + bool fDbl = areParamsDouble(argsCount, args); + + for (int i = 0; i < argsCount; ++i) + { + if (args[i]->isUnknown()) + { + if (fDbl) + args[i]->makeDouble(); + else + args[i]->makeDecimal128(); + } + } +} + + +void setParamsDecFloat(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc** args) +{ + bool f64 = areParamsDec64(argsCount, args); + + for (int i = 0; i < argsCount; ++i) + { + if (args[i]->isUnknown()) + { + if (f64) + args[i]->makeDecimal64(); + else + args[i]->makeDecimal128(); + } + } +} + + void setParamsFromList(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, int argsCount, dsc** args) { @@ -358,7 +436,7 @@ void setParamsDateAdd(DataTypeUtilBase*, const SysFunction*, int argsCount, dsc* if (argsCount >= 1 && args[0]->isUnknown()) { if (args[1]->dsc_address && // constant - CVT_get_long(args[1], 0, ERR_post) == blr_extract_millisecond) + CVT_get_long(args[1], 0, JRD_get_thread_data()->getAttachment()->att_dec_status, ERR_post) == blr_extract_millisecond) { args[0]->makeInt64(ISC_TIME_SECONDS_PRECISION_SCALE + 3); } @@ -490,6 +568,46 @@ void makeDoubleResult(DataTypeUtilBase*, const SysFunction*, dsc* result, } +void makeDblDecResult(DataTypeUtilBase*, const SysFunction*, dsc* result, + int argsCount, const dsc** args) +{ + if (argsCount == 0 || areParamsDouble(argsCount, args)) + result->makeDouble(); + else + result->makeDecimal128(); + + bool isNullable; + if (initResult(result, argsCount, args, &isNullable)) + return; + + result->setNullable(isNullable); +} + + +void makeDecFloatResult(DataTypeUtilBase*, const SysFunction*, dsc* result, + int argsCount, const dsc** args) +{ + if (argsCount == 0 || args[0]->dsc_dtype == dtype_dec128) + result->makeDecimal128(); + else + result->makeDecimal64(); + + bool isNullable; + if (initResult(result, argsCount, args, &isNullable)) + return; + + result->setNullable(isNullable); +} + + +void makePi(DataTypeUtilBase*, const SysFunction*, dsc* result, int, const dsc**) +{ + result->makeDouble(); + result->clearNull(); + result->setNullable(false); +} + + void makeFromListResult(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* result, int argsCount, const dsc** args) { @@ -595,6 +713,8 @@ void makeAbs(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* r case dtype_real: case dtype_double: case dtype_int64: + case dtype_dec64: + case dtype_dec128: *result = *value; break; @@ -736,6 +856,11 @@ void makeCeilFloor(DataTypeUtilBase*, const SysFunction* function, dsc* result, result->makeInt64(0); break; + case dtype_dec128: + case dtype_dec64: + result->makeDecimal128(0); + break; + default: result->makeDouble(); break; @@ -908,7 +1033,7 @@ void makePad(DataTypeUtilBase* dataTypeUtil, const SysFunction* function, dsc* r if (length->dsc_address) // constant { result->dsc_length = static_cast(sizeof(USHORT)) + dataTypeUtil->fixLength(result, - CVT_get_long(length, 0, ERR_post) * + CVT_get_long(length, 0, JRD_get_thread_data()->getAttachment()->att_dec_status, ERR_post) * dataTypeUtil->maxBytesPerChar(result->getCharSet())); } else @@ -1019,7 +1144,8 @@ void makeRound(DataTypeUtilBase*, const SysFunction* function, dsc* result, return; } - if (value1->isExact() || value1->dsc_dtype == dtype_real || value1->dsc_dtype == dtype_double) + if (value1->isExact() || value1->dsc_dtype == dtype_real || value1->dsc_dtype == dtype_double || + value1->dsc_dtype == dtype_dec64 || value1->dsc_dtype == dtype_dec128) { *result = *value1; if (argsCount == 1) @@ -1111,7 +1237,7 @@ dsc* evlStdMath(thread_db* tdbb, const SysFunction* function, const NestValueArr if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const double v = MOV_get_double(value); + const double v = MOV_get_double(tdbb, value); double rc; // CVC: Apparently, gcc has built-in inverse hyperbolic functions, but since @@ -1223,10 +1349,18 @@ dsc* evlAbs(thread_db* tdbb, const SysFunction*, const NestValueArray& args, imp impure->vlu_misc.vlu_double = fabs(impure->vlu_misc.vlu_double); break; + case dtype_dec64: + impure->vlu_misc.vlu_dec64 = impure->vlu_misc.vlu_dec64.abs(); + break; + + case dtype_dec128: + impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.abs(); + break; + case dtype_short: case dtype_long: case dtype_int64: - impure->vlu_misc.vlu_int64 = MOV_get_int64(value, value->dsc_scale); + impure->vlu_misc.vlu_int64 = MOV_get_int64(tdbb, value, value->dsc_scale); if (impure->vlu_misc.vlu_int64 == MIN_SINT64) status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); @@ -1237,7 +1371,7 @@ dsc* evlAbs(thread_db* tdbb, const SysFunction*, const NestValueArray& args, imp break; default: - impure->vlu_misc.vlu_double = fabs(MOV_get_double(&impure->vlu_desc)); + impure->vlu_misc.vlu_double = fabs(MOV_get_double(tdbb, &impure->vlu_desc)); impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); break; } @@ -1257,7 +1391,7 @@ dsc* evlAsciiChar(thread_db* tdbb, const SysFunction*, const NestValueArray& arg if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const SLONG code = MOV_get_long(value, 0); + const SLONG code = MOV_get_long(tdbb, value, 0); if (!(code >= 0 && code <= 255)) status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_numeric_out_of_range)); @@ -1318,8 +1452,8 @@ dsc* evlAtan2(thread_db* tdbb, const SysFunction* function, const NestValueArray if (request->req_flags & req_null) // return NULL if desc2 is NULL return NULL; - double value1 = MOV_get_double(desc1); - double value2 = MOV_get_double(desc2); + double value1 = MOV_get_double(tdbb, desc1); + double value2 = MOV_get_double(tdbb, desc2); if (value1 == 0 && value2 == 0) { @@ -1351,24 +1485,24 @@ dsc* evlBin(thread_db* tdbb, const SysFunction* function, const NestValueArray& if (i == 0) { if ((Function)(IPTR) function->misc == funBinNot) - impure->vlu_misc.vlu_int64 = ~MOV_get_int64(value, 0); + impure->vlu_misc.vlu_int64 = ~MOV_get_int64(tdbb, value, 0); else - impure->vlu_misc.vlu_int64 = MOV_get_int64(value, 0); + impure->vlu_misc.vlu_int64 = MOV_get_int64(tdbb, value, 0); } else { switch ((Function)(IPTR) function->misc) { case funBinAnd: - impure->vlu_misc.vlu_int64 &= MOV_get_int64(value, 0); + impure->vlu_misc.vlu_int64 &= MOV_get_int64(tdbb, value, 0); break; case funBinOr: - impure->vlu_misc.vlu_int64 |= MOV_get_int64(value, 0); + impure->vlu_misc.vlu_int64 |= MOV_get_int64(tdbb, value, 0); break; case funBinXor: - impure->vlu_misc.vlu_int64 ^= MOV_get_int64(value, 0); + impure->vlu_misc.vlu_int64 ^= MOV_get_int64(tdbb, value, 0); break; default: @@ -1399,7 +1533,7 @@ dsc* evlBinShift(thread_db* tdbb, const SysFunction* function, const NestValueAr if (request->req_flags & req_null) // return NULL if value2 is NULL return NULL; - const SINT64 shift = MOV_get_int64(value2, 0); + const SINT64 shift = MOV_get_int64(tdbb, value2, 0); if (shift < 0) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << @@ -1409,7 +1543,7 @@ dsc* evlBinShift(thread_db* tdbb, const SysFunction* function, const NestValueAr const SINT64 rotshift = shift % sizeof(SINT64); SINT64 tempbits = 0; - const SINT64 target = MOV_get_int64(value1, 0); + const SINT64 target = MOV_get_int64(tdbb, value1, 0); switch ((Function)(IPTR) function->misc) { @@ -1466,8 +1600,8 @@ dsc* evlCeil(thread_db* tdbb, const SysFunction*, const NestValueArray& args, for (int i = -impure->vlu_desc.dsc_scale; i > 0; --i) scale *= 10; - const SINT64 v1 = MOV_get_int64(&impure->vlu_desc, impure->vlu_desc.dsc_scale); - const SINT64 v2 = MOV_get_int64(&impure->vlu_desc, 0) * scale; + const SINT64 v1 = MOV_get_int64(tdbb, &impure->vlu_desc, impure->vlu_desc.dsc_scale); + const SINT64 v2 = MOV_get_int64(tdbb, &impure->vlu_desc, 0) * scale; impure->vlu_misc.vlu_int64 = v1 / scale; @@ -1483,13 +1617,23 @@ dsc* evlCeil(thread_db* tdbb, const SysFunction*, const NestValueArray& args, break; default: - impure->vlu_misc.vlu_double = MOV_get_double(&impure->vlu_desc); + impure->vlu_misc.vlu_double = MOV_get_double(tdbb, &impure->vlu_desc); // fall through case dtype_double: impure->vlu_misc.vlu_double = ceil(impure->vlu_misc.vlu_double); impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); break; + + case dtype_dec64: + impure->vlu_misc.vlu_dec64 = impure->vlu_misc.vlu_dec64.ceil(tdbb->getAttachment()->att_dec_status); + impure->vlu_desc.makeDecimal64(&impure->vlu_misc.vlu_dec64); + break; + + case dtype_dec128: + impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.ceil(tdbb->getAttachment()->att_dec_status); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + break; } return &impure->vlu_desc; @@ -1522,7 +1666,7 @@ dsc* evlCharToUuid(thread_db* tdbb, const SysFunction* function, const NestValue } UCHAR* data_temp; - USHORT len = MOV_get_string(value, &data_temp, NULL, 0); + USHORT len = MOV_get_string(tdbb, value, &data_temp, NULL, 0); const UCHAR* data; if (len > GUID_BODY_SIZE) @@ -1651,7 +1795,7 @@ dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArr if (request->req_flags & req_null) // return NULL if valueDsc is NULL return NULL; - const SLONG part = MOV_get_long(partDsc, 0); + const SLONG part = MOV_get_long(tdbb, partDsc, 0); TimeStamp timestamp; @@ -1701,7 +1845,7 @@ dsc* evlDateAdd(thread_db* tdbb, const SysFunction* function, const NestValueArr static const SSHORT milliScale = ISC_TIME_SECONDS_PRECISION_SCALE + 3; static const int milliPow = NoThrowTimeStamp::POW_10_TABLE[-milliScale]; - const SINT64 quantity = MOV_get_int64(quantityDsc, + const SINT64 quantity = MOV_get_int64(tdbb, quantityDsc, (part == blr_extract_millisecond ? milliScale : 0)); switch (part) @@ -1911,7 +2055,7 @@ dsc* evlDateDiff(thread_db* tdbb, const SysFunction* function, const NestValueAr timestamp1.decode(×1); timestamp2.decode(×2); - const SLONG part = MOV_get_long(partDsc, 0); + const SLONG part = MOV_get_long(tdbb, partDsc, 0); switch (part) { @@ -2062,14 +2206,27 @@ dsc* evlExp(thread_db* tdbb, const SysFunction*, const NestValueArray& args, if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const double rc = exp(MOV_get_double(value)); - if (rc == HUGE_VAL) // unlikely to trap anything - status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); - if (isinf(rc)) - status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); + if (value->isDecFloat()) + { + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + impure->vlu_misc.vlu_dec128 = MOV_get_dec128(tdbb, value); - impure->vlu_misc.vlu_double = rc; - impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + Decimal128 e; + e.set("2.718281828459045235360287471352662497757", decSt); + impure->vlu_misc.vlu_dec128 = e.pow(decSt, impure->vlu_misc.vlu_dec128); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + } + else + { + const double rc = exp(MOV_get_double(tdbb, value)); + if (rc == HUGE_VAL) // unlikely to trap anything + status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); + if (isinf(rc)) + status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); + + impure->vlu_misc.vlu_double = rc; + impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + } return &impure->vlu_desc; } @@ -2100,8 +2257,8 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args, for (int i = -impure->vlu_desc.dsc_scale; i > 0; --i) scale *= 10; - const SINT64 v1 = MOV_get_int64(&impure->vlu_desc, impure->vlu_desc.dsc_scale); - const SINT64 v2 = MOV_get_int64(&impure->vlu_desc, 0) * scale; + const SINT64 v1 = MOV_get_int64(tdbb, &impure->vlu_desc, impure->vlu_desc.dsc_scale); + const SINT64 v2 = MOV_get_int64(tdbb, &impure->vlu_desc, 0) * scale; impure->vlu_misc.vlu_int64 = v1 / scale; @@ -2117,13 +2274,23 @@ dsc* evlFloor(thread_db* tdbb, const SysFunction*, const NestValueArray& args, break; default: - impure->vlu_misc.vlu_double = MOV_get_double(&impure->vlu_desc); + impure->vlu_misc.vlu_double = MOV_get_double(tdbb, &impure->vlu_desc); // fall through case dtype_double: impure->vlu_misc.vlu_double = floor(impure->vlu_misc.vlu_double); impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); break; + + case dtype_dec64: + impure->vlu_misc.vlu_dec64 = impure->vlu_misc.vlu_dec64.floor(tdbb->getAttachment()->att_dec_status); + impure->vlu_desc.makeDecimal64(&impure->vlu_misc.vlu_dec64); + break; + + case dtype_dec128: + impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.floor(tdbb->getAttachment()->att_dec_status); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + break; } return &impure->vlu_desc; @@ -2566,32 +2733,63 @@ dsc* evlLnLog10(thread_db* tdbb, const SysFunction* function, const NestValueArr if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const double v = MOV_get_double(value); - - if (v <= 0) + if (value->isDecFloat()) { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_sysf_argmustbe_positive) << - Arg::Str(function->name)); + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + Decimal128 d = MOV_get_dec128(tdbb, value); + + if (d.compare(decSt, CDecimal128(0)) <= 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_argmustbe_positive) << + Arg::Str(function->name)); + } + + switch ((Function)(IPTR) function->misc) + { + case funLnat: + d = d.ln(decSt); + break; + case funLog10: + d = d.log10(decSt); + break; + default: + fb_assert(0); + return NULL; + } + + impure->vlu_misc.vlu_dec128 = d; + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); } - - double rc; - - switch ((Function)(IPTR) function->misc) + else { - case funLnat: - rc = log(v); - break; - case funLog10: - rc = log10(v); - break; - default: - fb_assert(0); - return NULL; - } + const double v = MOV_get_double(tdbb, value); - impure->vlu_misc.vlu_double = rc; - impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + if (v <= 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_argmustbe_positive) << + Arg::Str(function->name)); + } + + double rc; + + switch ((Function)(IPTR) function->misc) + { + case funLnat: + rc = log(v); + break; + case funLog10: + rc = log10(v); + break; + default: + fb_assert(0); + return NULL; + } + + impure->vlu_misc.vlu_double = rc; + impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + } return &impure->vlu_desc; } @@ -2604,33 +2802,188 @@ dsc* evlLog(thread_db* tdbb, const SysFunction* function, const NestValueArray& jrd_req* request = tdbb->getRequest(); - const dsc* value1 = EVL_expr(tdbb, request, args[0]); - if (request->req_flags & req_null) // return NULL if value1 is NULL + const dsc* value[2]; + value[0] = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const dsc* value2 = EVL_expr(tdbb, request, args[1]); - if (request->req_flags & req_null) // return NULL if value2 is NULL + value[1] = EVL_expr(tdbb, request, args[1]); + if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const double v1 = MOV_get_double(value1); - const double v2 = MOV_get_double(value2); - - if (v1 <= 0) + if (!areParamsDouble(2, value)) { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_sysf_basemustbe_positive) << - Arg::Str(function->name)); + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + Decimal128 v1 = MOV_get_dec128(tdbb, value[0]); + Decimal128 v2 = MOV_get_dec128(tdbb, value[1]); + + if (v1.compare(decSt, CDecimal128(0)) <= 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_basemustbe_positive) << + Arg::Str(function->name)); + } + + if (v2.compare(decSt, CDecimal128(0)) <= 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_argmustbe_positive) << + Arg::Str(function->name)); + } + + impure->vlu_misc.vlu_dec128 = v2.ln(decSt).div(decSt, v1.ln(decSt)); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + } + else + { + const double v1 = MOV_get_double(tdbb, value[0]); + const double v2 = MOV_get_double(tdbb, value[1]); + + if (v1 <= 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_basemustbe_positive) << + Arg::Str(function->name)); + } + + if (v2 <= 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_argmustbe_positive) << + Arg::Str(function->name)); + } + + impure->vlu_misc.vlu_double = log(v2) / log(v1); + impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); } - if (v2 <= 0) + return &impure->vlu_desc; +} + + +dsc* evlQuantize(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure) +{ + fb_assert(args.getCount() == 2); + + jrd_req* request = tdbb->getRequest(); + + const dsc* value[2]; + value[0] = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + value[1] = EVL_expr(tdbb, request, args[1]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + + if (value[0]->dsc_dtype == dtype_dec64) { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_sysf_argmustbe_positive) << - Arg::Str(function->name)); + Decimal64 v1 = MOV_get_dec64(tdbb, value[0]); + Decimal64 v2 = MOV_get_dec64(tdbb, value[1]); + + impure->vlu_misc.vlu_dec64 = v1.quantize(decSt, v2); + impure->vlu_desc.makeDecimal64(&impure->vlu_misc.vlu_dec64); + } + else + { + Decimal128 v1 = MOV_get_dec128(tdbb, value[0]); + Decimal128 v2 = MOV_get_dec128(tdbb, value[1]); + + impure->vlu_misc.vlu_dec128 = v1.quantize(decSt, v2); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); } - impure->vlu_misc.vlu_double = log(v2) / log(v1); - impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + return &impure->vlu_desc; +} + + +dsc* evlCompare(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure) +{ + fb_assert(args.getCount() == 2); + + jrd_req* request = tdbb->getRequest(); + + const dsc* value[2]; + value[0] = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + value[1] = EVL_expr(tdbb, request, args[1]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + if (value[0]->dsc_dtype == dtype_dec64) + { + Decimal64 v1 = MOV_get_dec64(tdbb, value[0]); + Decimal64 v2 = MOV_get_dec64(tdbb, value[1]); + + switch ((Function)(IPTR) function->misc) + { + case funTotalOrd: + impure->vlu_misc.vlu_short = v1.totalOrder(v2); + break; + case funCmpDec: + impure->vlu_misc.vlu_short = v1.decCompare(v2); + break; + default: + fb_assert(false); + } + } + else + { + Decimal128 v1 = MOV_get_dec128(tdbb, value[0]); + Decimal128 v2 = MOV_get_dec128(tdbb, value[1]); + + switch ((Function)(IPTR) function->misc) + { + case funTotalOrd: + impure->vlu_misc.vlu_short = v1.totalOrder(v2); + break; + case funCmpDec: + impure->vlu_misc.vlu_short = v1.decCompare(v2); + break; + default: + fb_assert(false); + } + } + + impure->vlu_desc.makeShort(0, &impure->vlu_misc.vlu_short); + return &impure->vlu_desc; +} + + +dsc* evlNormDec(thread_db* tdbb, const SysFunction* function, const NestValueArray& args, + impure_value* impure) +{ + fb_assert(args.getCount() == 1); + + jrd_req* request = tdbb->getRequest(); + + const dsc* value; + value = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL + return NULL; + + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + + if (value->dsc_dtype == dtype_dec64) + { + Decimal64 v = MOV_get_dec64(tdbb, value); + + impure->vlu_misc.vlu_dec64 = v.normalize(decSt); + impure->vlu_desc.makeDecimal64(&impure->vlu_misc.vlu_dec64); + } + else + { + Decimal128 v = MOV_get_dec128(tdbb, value); + + impure->vlu_misc.vlu_dec128 = v.normalize(decSt); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + } return &impure->vlu_desc; } @@ -2658,12 +3011,12 @@ dsc* evlMaxMinValue(thread_db* tdbb, const SysFunction* function, const NestValu switch ((Function)(IPTR) function->misc) { case funMaxValue: - if (MOV_compare(value, result) > 0) + if (MOV_compare(tdbb, value, result) > 0) result = value; break; case funMinValue: - if (MOV_compare(value, result) < 0) + if (MOV_compare(tdbb, value, result) < 0) result = value; break; @@ -2695,12 +3048,12 @@ dsc* evlMod(thread_db* tdbb, const SysFunction*, const NestValueArray& args, EVL_make_value(tdbb, value1, impure); impure->vlu_desc.dsc_scale = 0; - const SINT64 divisor = MOV_get_int64(value2, 0); + const SINT64 divisor = MOV_get_int64(tdbb, value2, 0); if (divisor == 0) status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_integer_divide_by_zero)); - const SINT64 result = MOV_get_int64(value1, 0) % divisor; + const SINT64 result = MOV_get_int64(tdbb, value1, 0) % divisor; switch (impure->vlu_desc.dsc_dtype) { @@ -2754,7 +3107,7 @@ dsc* evlOverlay(thread_db* tdbb, const SysFunction* function, const NestValueArr if (request->req_flags & req_null) // return NULL if lengthDsc is NULL return NULL; - const SLONG auxlen = MOV_get_long(lengthDsc, 0); + const SLONG auxlen = MOV_get_long(tdbb, lengthDsc, 0); if (auxlen < 0) { @@ -2767,7 +3120,7 @@ dsc* evlOverlay(thread_db* tdbb, const SysFunction* function, const NestValueArr length = auxlen; } - SLONG from = MOV_get_long(fromDsc, 0); + SLONG from = MOV_get_long(tdbb, fromDsc, 0); if (from <= 0) { @@ -2925,7 +3278,7 @@ dsc* evlPad(thread_db* tdbb, const SysFunction* function, const NestValueArray& if (request->req_flags & req_null) // return NULL if padLenDsc is NULL return NULL; - const SLONG padLenArg = MOV_get_long(padLenDsc, 0); + const SLONG padLenArg = MOV_get_long(tdbb, padLenDsc, 0); if (padLenArg < 0) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << @@ -3108,7 +3461,7 @@ dsc* evlPosition(thread_db* tdbb, const SysFunction* function, const NestValueAr if (request->req_flags & req_null) // return NULL if value3 is NULL return NULL; - start = MOV_get_long(value3, 0); + start = MOV_get_long(tdbb, value3, 0); if (start <= 0) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << @@ -3218,42 +3571,55 @@ dsc* evlPower(thread_db* tdbb, const SysFunction* function, const NestValueArray jrd_req* request = tdbb->getRequest(); - const dsc* value1 = EVL_expr(tdbb, request, args[0]); - if (request->req_flags & req_null) // return NULL if value1 is NULL + const dsc* value[2]; + value[0] = EVL_expr(tdbb, request, args[0]); + if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const dsc* value2 = EVL_expr(tdbb, request, args[1]); - if (request->req_flags & req_null) // return NULL if value2 is NULL + value[1] = EVL_expr(tdbb, request, args[1]); + if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); - - const double v1 = MOV_get_double(value1); - const double v2 = MOV_get_double(value2); - - if (v1 == 0 && v2 < 0) + if (!areParamsDouble(2, value)) { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_sysf_invalid_zeropowneg) << - Arg::Str(function->name)); - } + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + impure->vlu_misc.vlu_dec128 = MOV_get_dec128(tdbb, value[0]); + Decimal128 v2 = MOV_get_dec128(tdbb, value[1]); - if (v1 < 0 && - (!value2->isExact() || - MOV_get_int64(value2, 0) * SINT64(CVT_power_of_ten(-value2->dsc_scale)) != - MOV_get_int64(value2, value2->dsc_scale))) + impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.pow(decSt, v2); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + } + else { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_sysf_invalid_negpowfp) << - Arg::Str(function->name)); + impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + + const double v1 = MOV_get_double(tdbb, value[0]); + const double v2 = MOV_get_double(tdbb, value[1]); + + if (v1 == 0 && v2 < 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_invalid_zeropowneg) << + Arg::Str(function->name)); + } + + if (v1 < 0 && + (!value[1]->isExact() || + MOV_get_int64(tdbb, value[1], 0) * SINT64(CVT_power_of_ten(-value[1]->dsc_scale)) != + MOV_get_int64(tdbb, value[1], value[1]->dsc_scale))) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_invalid_negpowfp) << + Arg::Str(function->name)); + } + + const double rc = pow(v1, v2); + if (isinf(rc)) + status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); + + impure->vlu_misc.vlu_double = rc; } - const double rc = pow(v1, v2); - if (isinf(rc)) - status_exception::raise(Arg::Gds(isc_arith_except) << Arg::Gds(isc_exception_float_overflow)); - - impure->vlu_misc.vlu_double = rc; - return &impure->vlu_desc; } @@ -3578,7 +3944,7 @@ dsc* evlRight(thread_db* tdbb, const SysFunction*, const NestValueArray& args, start = charSet->length(start, p, true); } - start -= MOV_get_long(len, 0); + start -= MOV_get_long(tdbb, len, 0); start = MAX(0, start); dsc startDsc; @@ -3607,7 +3973,7 @@ dsc* evlRound(thread_db* tdbb, const SysFunction* function, const NestValueArray if (request->req_flags & req_null) // return NULL if scaleDsc is NULL return NULL; - scale = -MOV_get_long(scaleDsc, 0); + scale = -MOV_get_long(tdbb, scaleDsc, 0); if (!(scale >= MIN_SCHAR && scale <= MAX_SCHAR)) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << @@ -3616,7 +3982,7 @@ dsc* evlRound(thread_db* tdbb, const SysFunction* function, const NestValueArray } } - impure->vlu_misc.vlu_int64 = MOV_get_int64(value, scale); + impure->vlu_misc.vlu_int64 = MOV_get_int64(tdbb, value, scale); impure->vlu_desc.makeInt64(scale, &impure->vlu_misc.vlu_int64); return &impure->vlu_desc; @@ -3634,14 +4000,19 @@ dsc* evlSign(thread_db* tdbb, const SysFunction*, const NestValueArray& args, if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - const double val = MOV_get_double(value); + if (value->isDecFloat()) + impure->vlu_misc.vlu_short = MOV_get_dec128(tdbb, value).sign(); + else + { + const double val = MOV_get_double(tdbb, value); - if (val > 0) - impure->vlu_misc.vlu_short = 1; - else if (val < 0) - impure->vlu_misc.vlu_short = -1; - else // val == 0 - impure->vlu_misc.vlu_short = 0; + if (val > 0) + impure->vlu_misc.vlu_short = 1; + else if (val < 0) + impure->vlu_misc.vlu_short = -1; + else // val == 0 + impure->vlu_misc.vlu_short = 0; + } impure->vlu_desc.makeShort(0, &impure->vlu_misc.vlu_short); @@ -3660,16 +4031,33 @@ dsc* evlSqrt(thread_db* tdbb, const SysFunction* function, const NestValueArray& if (request->req_flags & req_null) // return NULL if value is NULL return NULL; - impure->vlu_misc.vlu_double = MOV_get_double(value); - - if (impure->vlu_misc.vlu_double < 0) + if (value->isDecFloat()) { - status_exception::raise(Arg::Gds(isc_expression_eval_err) << - Arg::Gds(isc_sysf_argmustbe_nonneg) << Arg::Str(function->name)); - } + DecimalStatus decSt = tdbb->getAttachment()->att_dec_status; + impure->vlu_misc.vlu_dec128 = MOV_get_dec128(tdbb, value); - impure->vlu_misc.vlu_double = sqrt(impure->vlu_misc.vlu_double); - impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + if (impure->vlu_misc.vlu_dec128.compare(decSt, CDecimal128(0)) < 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_argmustbe_nonneg) << Arg::Str(function->name)); + } + + impure->vlu_misc.vlu_dec128 = impure->vlu_misc.vlu_dec128.sqrt(decSt); + impure->vlu_desc.makeDecimal128(&impure->vlu_misc.vlu_dec128); + } + else + { + impure->vlu_misc.vlu_double = MOV_get_double(tdbb, value); + + if (impure->vlu_misc.vlu_double < 0) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_sysf_argmustbe_nonneg) << Arg::Str(function->name)); + } + + impure->vlu_misc.vlu_double = sqrt(impure->vlu_misc.vlu_double); + impure->vlu_desc.makeDouble(&impure->vlu_misc.vlu_double); + } return &impure->vlu_desc; } @@ -3693,7 +4081,7 @@ dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray if (request->req_flags & req_null) // return NULL if scaleDsc is NULL return NULL; - resultScale = -MOV_get_long(scaleDsc, 0); + resultScale = -MOV_get_long(tdbb, scaleDsc, 0); if (!(resultScale >= MIN_SCHAR && resultScale <= MAX_SCHAR)) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << @@ -3705,7 +4093,7 @@ dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray if (value->isExact()) { SSHORT scale = value->dsc_scale; - impure->vlu_misc.vlu_int64 = MOV_get_int64(value, scale); + impure->vlu_misc.vlu_int64 = MOV_get_int64(tdbb, value, scale); if (resultScale < scale) resultScale = scale; @@ -3725,7 +4113,7 @@ dsc* evlTrunc(thread_db* tdbb, const SysFunction* function, const NestValueArray } else { - impure->vlu_misc.vlu_double = MOV_get_double(value); + impure->vlu_misc.vlu_double = MOV_get_double(tdbb, value); SINT64 v = 1; @@ -3781,7 +4169,7 @@ dsc* evlUuidToChar(thread_db* tdbb, const SysFunction* function, const NestValue } UCHAR* data; - const USHORT len = MOV_get_string(value, &data, NULL, 0); + const USHORT len = MOV_get_string(tdbb, value, &data, NULL, 0); if (len != sizeof(Guid)) { @@ -3856,7 +4244,7 @@ dsc* evlSystemPrivilege(thread_db* tdbb, const SysFunction*, const NestValueArra const SysFunction SysFunction::functions[] = { - {"ABS", 1, 1, setParamsDouble, makeAbs, evlAbs, NULL}, + {"ABS", 1, 1, setParamsDblDec, makeAbs, evlAbs, NULL}, {"ACOS", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcos}, {"ACOSH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfAcosh}, {"ASCII_CHAR", 1, 1, setParamsInteger, makeAsciiChar, evlAsciiChar, NULL}, @@ -3874,30 +4262,33 @@ const SysFunction SysFunction::functions[] = {"BIN_SHL_ROT", 2, 2, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShlRot}, {"BIN_SHR_ROT", 2, 2, setParamsInteger, makeBinShift, evlBinShift, (void*) funBinShrRot}, {"BIN_XOR", 2, -1, setParamsInteger, makeBin, evlBin, (void*) funBinXor}, - {"CEIL", 1, 1, setParamsDouble, makeCeilFloor, evlCeil, NULL}, - {"CEILING", 1, 1, setParamsDouble, makeCeilFloor, evlCeil, NULL}, + {"CEIL", 1, 1, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, + {"CEILING", 1, 1, setParamsDblDec, makeCeilFloor, evlCeil, NULL}, {"CHAR_TO_UUID", 1, 1, setParamsCharToUuid, makeUuid, evlCharToUuid, NULL}, + {"COMPARE_DECFLOAT", 2, 2, setParamsDecFloat, makeShortResult, evlCompare, (void*) funCmpDec}, {"COS", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCos}, {"COSH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCosh}, {"COT", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfCot}, {"DATEADD", 3, 3, setParamsDateAdd, makeDateAdd, evlDateAdd, NULL}, {"DATEDIFF", 3, 3, setParamsDateDiff, makeInt64Result, evlDateDiff, NULL}, - {"EXP", 1, 1, setParamsDouble, makeDoubleResult, evlExp, NULL}, - {"FLOOR", 1, 1, setParamsDouble, makeCeilFloor, evlFloor, NULL}, + {"EXP", 1, 1, setParamsDblDec, makeDblDecResult, evlExp, NULL}, + {"FLOOR", 1, 1, setParamsDblDec, makeCeilFloor, evlFloor, NULL}, {"GEN_UUID", 0, 0, NULL, makeUuid, evlGenUuid, NULL}, {"HASH", 1, 1, NULL, makeInt64Result, evlHash, NULL}, {"LEFT", 2, 2, setParamsSecondInteger, makeLeftRight, evlLeft, NULL}, - {"LN", 1, 1, setParamsDouble, makeDoubleResult, evlLnLog10, (void*) funLnat}, - {"LOG", 2, 2, setParamsDouble, makeDoubleResult, evlLog, NULL}, - {"LOG10", 1, 1, setParamsDouble, makeDoubleResult, evlLnLog10, (void*) funLog10}, + {"LN", 1, 1, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLnat}, + {"LOG", 2, 2, setParamsDblDec, makeDblDecResult, evlLog, NULL}, + {"LOG10", 1, 1, setParamsDblDec, makeDblDecResult, evlLnLog10, (void*) funLog10}, {"LPAD", 2, 3, setParamsSecondInteger, makePad, evlPad, (void*) funLPad}, {"MAXVALUE", 1, -1, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMaxValue}, {"MINVALUE", 1, -1, setParamsFromList, makeFromListResult, evlMaxMinValue, (void*) funMinValue}, {"MOD", 2, 2, setParamsFromList, makeMod, evlMod, NULL}, + {"NORMALIZE_DECFLOAT", 1, 1, setParamsDecFloat, makeDecFloatResult, evlNormDec, NULL}, {"OVERLAY", 3, 4, setParamsOverlay, makeOverlay, evlOverlay, NULL}, - {"PI", 0, 0, NULL, makeDoubleResult, evlPi, NULL}, + {"PI", 0, 0, NULL, makePi, evlPi, NULL}, {"POSITION", 2, 3, setParamsPosition, makeLongResult, evlPosition, NULL}, - {"POWER", 2, 2, setParamsDouble, makeDoubleResult, evlPower, NULL}, + {"POWER", 2, 2, setParamsDblDec, makeDblDecResult, evlPower, NULL}, + {"QUANTIZE", 2, 2, setParamsDecFloat, makeDecFloatResult, evlQuantize, NULL}, {"RAND", 0, 0, NULL, makeDoubleResult, evlRand, NULL}, {RDB_GET_CONTEXT, 2, 2, setParamsGetSetContext, makeGetSetContext, evlGetContext, NULL}, {"RDB$ROLE_IN_USE", 1, 1, setParamsAsciiVal, makeBooleanResult, evlRoleInUse, NULL}, @@ -3908,12 +4299,13 @@ const SysFunction SysFunction::functions[] = {"RIGHT", 2, 2, setParamsSecondInteger, makeLeftRight, evlRight, NULL}, {"ROUND", 1, 2, setParamsRoundTrunc, makeRound, evlRound, NULL}, {"RPAD", 2, 3, setParamsSecondInteger, makePad, evlPad, (void*) funRPad}, - {"SIGN", 1, 1, setParamsDouble, makeShortResult, evlSign, NULL}, + {"SIGN", 1, 1, setParamsDblDec, makeShortResult, evlSign, NULL}, {"SIN", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSin}, {"SINH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfSinh}, - {"SQRT", 1, 1, setParamsDouble, makeDoubleResult, evlSqrt, NULL}, + {"SQRT", 1, 1, setParamsDblDec, makeDblDecResult, evlSqrt, NULL}, {"TAN", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTan}, {"TANH", 1, 1, setParamsDouble, makeDoubleResult, evlStdMath, (void*) trfTanh}, + {"TOTALORDER", 2, 2, setParamsDecFloat, makeShortResult, evlCompare, (void*) funTotalOrd}, {"TRUNC", 1, 2, setParamsRoundTrunc, makeTrunc, evlTrunc, NULL}, {"UUID_TO_CHAR", 1, 1, setParamsUuidToChar, makeUuidToChar, evlUuidToChar, NULL}, {"", 0, 0, NULL, NULL, NULL, NULL} diff --git a/src/jrd/VirtualTable.cpp b/src/jrd/VirtualTable.cpp index 03e304825c..7a782f5cb4 100644 --- a/src/jrd/VirtualTable.cpp +++ b/src/jrd/VirtualTable.cpp @@ -70,7 +70,7 @@ void VirtualTable::erase(thread_db* tdbb, record_param* rpb) // Ignore attempt to stop system attachment dsc sysFlag; if (EVL_field(relation, rpb->rpb_record, f_mon_att_sys_flag, &sysFlag) && - MOV_get_long(&sysFlag, 0) != 0) + MOV_get_long(tdbb, &sysFlag, 0) != 0) { return; } @@ -90,7 +90,7 @@ void VirtualTable::erase(thread_db* tdbb, record_param* rpb) return; } - const SINT64 id = MOV_get_int64(&desc, 0); + const SINT64 id = MOV_get_int64(tdbb, &desc, 0); // Post a blocking request Lock temp_lock(tdbb, sizeof(SINT64), lock_type); diff --git a/src/jrd/align.h b/src/jrd/align.h index a93e556b72..66d739915c 100644 --- a/src/jrd/align.h +++ b/src/jrd/align.h @@ -32,6 +32,7 @@ Maximum alignments for corresponding data types are defined in dsc.h */ +#include "../common/DecFloat.h" #include "../jrd/blr.h" /* The following macro must be defined as the highest-numericly-valued @@ -67,7 +68,9 @@ static const USHORT gds_cvt_blr_dtype[DTYPE_BLR_MAX + 1] = dtype_int64, /* blr_int64 == 16 */ 0, 0, 0, 0, 0, 0, dtype_boolean, // blr_bool == 23 - 0, 0, 0, + dtype_dec64, /* blr_dec64 == 24 */ + dtype_dec128, /* blr_dec128 == 25 */ + 0, dtype_double, /* blr_double == 27 */ 0, 0, 0, 0, 0, 0, 0, dtype_timestamp, /* blr_timestamp == 35 */ @@ -103,7 +106,9 @@ static const USHORT type_alignments[DTYPE_TYPE_MAX] = sizeof(SLONG), /* dtype_array */ sizeof(SINT64), /* dtype_int64 */ sizeof(ULONG), /* dtype_dbkey */ - sizeof(UCHAR) /* dtype_boolean */ + sizeof(UCHAR), /* dtype_boolean */ + sizeof(Firebird::Decimal64),/* dtype_dec64 */ + sizeof(Firebird::Decimal64) /* dtype_dec128 */ }; static const USHORT type_lengths[DTYPE_TYPE_MAX] = @@ -129,7 +134,9 @@ static const USHORT type_lengths[DTYPE_TYPE_MAX] = sizeof(ISC_QUAD), /* dtype_array */ sizeof(SINT64), /* dtype_int64 */ sizeof(RecordNumber::Packed), /*dtype_dbkey */ - sizeof(UCHAR) /* dtype_boolean */ + sizeof(UCHAR), /* dtype_boolean */ + sizeof(Firebird::Decimal64),/* dtype_dec64 */ + sizeof(Firebird::Decimal128)/* dtype_dec128 */ }; @@ -158,7 +165,9 @@ static const USHORT type_significant_bits[DTYPE_TYPE_MAX] = sizeof(ISC_QUAD) * 8, /* dtype_array */ sizeof(SINT64) * 8, /* dtype_int64 */ 0, // dtype_dbkey - 0 // dtype_boolean + 0, // dtype_boolean + 0, // dtype_dec64 + 0 // dtype_dec128 }; #endif /* JRD_ALIGN_H */ diff --git a/src/jrd/blb.cpp b/src/jrd/blb.cpp index 6dbb7221e0..e0febf31fd 100644 --- a/src/jrd/blb.cpp +++ b/src/jrd/blb.cpp @@ -999,13 +999,13 @@ void blb::move(thread_db* tdbb, dsc* from_desc, dsc* to_desc, const ValueExprNod if (field) { - if ((fieldNode = ExprNode::as(field))) + if ((fieldNode = nodeAs(field))) { // We should not materialize the blob if the destination field // stream (nod_union, for example) doesn't have a relation. simpleMove = tdbb->getRequest()->req_rpb[fieldNode->fieldStream].rpb_relation == NULL; } - else if (!(ExprNode::is(field) || ExprNode::is(field))) + else if (!(nodeIs(field) || nodeIs(field))) BUGCHECK(199); // msg 199 expected field node } @@ -2791,7 +2791,7 @@ static void slice_callback(array_slice* arg, ULONG /*count*/, DSC* descriptors) DynamicVaryStr<1024> tmp_buffer; const USHORT tmp_len = array_desc->dsc_length; const char* p; - const USHORT len = MOV_make_string(slice_desc, INTL_TEXT_TYPE(*array_desc), &p, + const USHORT len = MOV_make_string(tdbb, slice_desc, INTL_TEXT_TYPE(*array_desc), &p, tmp_buffer.getBuffer(tmp_len), tmp_len); memcpy(array_desc->dsc_address, &len, sizeof(USHORT)); memcpy(array_desc->dsc_address + sizeof(USHORT), p, (int) len); diff --git a/src/jrd/blr.h b/src/jrd/blr.h index b586a5e10f..912af5e809 100644 --- a/src/jrd/blr.h +++ b/src/jrd/blr.h @@ -59,7 +59,7 @@ #define blr_blob_id (unsigned char)45 /* added from gds.h */ #define blr_sql_date (unsigned char)12 #define blr_sql_time (unsigned char)13 -#define blr_int64 (unsigned char)16 +#define blr_int64 (unsigned char)16 #define blr_blob2 (unsigned char)17 #define blr_domain_name (unsigned char)18 #define blr_domain_name2 (unsigned char)19 @@ -67,6 +67,8 @@ #define blr_column_name (unsigned char)21 #define blr_column_name2 (unsigned char)22 #define blr_bool (unsigned char)23 +#define blr_dec64 (unsigned char)24 +#define blr_dec128 (unsigned char)25 // first sub parameter for blr_domain_name[2] #define blr_domain_type_of (unsigned char)0 diff --git a/src/jrd/btr.cpp b/src/jrd/btr.cpp index 707e411b2b..97a05f4ff4 100644 --- a/src/jrd/btr.cpp +++ b/src/jrd/btr.cpp @@ -1373,6 +1373,10 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx) length = sizeof(UCHAR); break; + case idx_decimal: + length = Decimal128::getIndexKeyLength(); + break; + default: if (idx->idx_flags & idx_expressn) { @@ -1425,6 +1429,9 @@ USHORT BTR_key_length(thread_db* tdbb, jrd_rel* relation, index_desc* idx) case idx_boolean: length = sizeof(UCHAR); break; + case idx_decimal: + length = Decimal128::getIndexKeyLength(); + break; default: length = format->fmt_desc[tail->idx_field].dsc_length; if (format->fmt_desc[tail->idx_field].dsc_dtype == dtype_varying) @@ -2219,7 +2226,7 @@ bool BTR_types_comparable(const dsc& target, const dsc& source) return (source.dsc_dtype <= dtype_long || source.dsc_dtype == dtype_int64); if (DTYPE_IS_NUMERIC(target.dsc_dtype)) - return (source.dsc_dtype <= dtype_double || source.dsc_dtype == dtype_int64); + return (source.dsc_dtype <= dtype_double || source.dsc_dtype == dtype_int64); //!!!!!!!! if (target.dsc_dtype == dtype_sql_date) return (source.dsc_dtype <= dtype_sql_date || source.dsc_dtype == dtype_timestamp); @@ -2424,7 +2431,7 @@ static void compress(thread_db* tdbb, } if (itype == idx_string || itype == idx_byte_array || itype == idx_metadata || - itype >= idx_first_intl_string) + itype == idx_decimal || itype >= idx_first_intl_string) { VaryStr buffer; const UCHAR pad = (itype == idx_string) ? ' ' : 0; @@ -2432,7 +2439,13 @@ static void compress(thread_db* tdbb, size_t length; - if (itype >= idx_first_intl_string || itype == idx_metadata) + if (itype == idx_decimal) + { + Decimal128 dec = MOV_get_dec128(tdbb, desc); + length = dec.makeIndexKey(&buffer); + ptr = reinterpret_cast(buffer.vary_string); + } + else if (itype >= idx_first_intl_string || itype == idx_metadata) { DSC to; @@ -2447,7 +2460,7 @@ static void compress(thread_db* tdbb, length = INTL_string_to_key(tdbb, itype, desc, &to, key_type); } else - length = MOV_get_string(desc, &ptr, &buffer, MAX_KEY); + length = MOV_get_string(tdbb, desc, &ptr, &buffer, MAX_KEY); if (length) { @@ -2501,7 +2514,7 @@ static void compress(thread_db* tdbb, if (itype == idx_numeric) { - temp.temp_double = MOV_get_double(desc); + temp.temp_double = MOV_get_double(tdbb, desc); temp_is_negative = (temp.temp_double < 0); #ifdef DEBUG_INDEXKEY @@ -2511,7 +2524,7 @@ static void compress(thread_db* tdbb, else if (itype == idx_numeric2) { int64_key_op = true; - temp.temp_int64_key = make_int64_key(MOV_get_int64(desc, desc->dsc_scale), desc->dsc_scale); + temp.temp_int64_key = make_int64_key(MOV_get_int64(tdbb, desc, desc->dsc_scale), desc->dsc_scale); temp_copy_length = sizeof(temp.temp_int64_key.d_part); temp_is_negative = (temp.temp_int64_key.d_part < 0); @@ -2586,7 +2599,7 @@ static void compress(thread_db* tdbb, } else { - temp.temp_double = MOV_get_double(desc); + temp.temp_double = MOV_get_double(tdbb, desc); temp_is_negative = (temp.temp_double < 0); #ifdef DEBUG_INDEXKEY diff --git a/src/jrd/btr.h b/src/jrd/btr.h index 0459205c91..e930584669 100644 --- a/src/jrd/btr.h +++ b/src/jrd/btr.h @@ -99,6 +99,7 @@ const int idx_sql_time = 6; const int idx_timestamp = 7; const int idx_numeric2 = 8; // Introduced for 64-bit Integer support const int idx_boolean = 9; +const int idx_decimal = 10; // idx_itype space for future expansion const int idx_first_intl_string = 64; // .. MAX (short) Range of computed key strings diff --git a/src/jrd/build_no.h b/src/jrd/build_no.h index 345837b0ec..a457bfe524 100644 --- a/src/jrd/build_no.h +++ b/src/jrd/build_no.h @@ -3,16 +3,16 @@ *** DO NOT EDIT *** TO CHANGE ANY INFORMATION IN HERE PLEASE EDIT src/misc/writeBuildNum.sh - FORMAL BUILD NUMBER:582 + FORMAL BUILD NUMBER:686 */ -#define PRODUCT_VER_STRING "4.0.0.582" -#define FILE_VER_STRING "WI-T4.0.0.582" -#define LICENSE_VER_STRING "WI-T4.0.0.582" -#define FILE_VER_NUMBER 4, 0, 0, 582 +#define PRODUCT_VER_STRING "4.0.0.686" +#define FILE_VER_STRING "WI-T4.0.0.686" +#define LICENSE_VER_STRING "WI-T4.0.0.686" +#define FILE_VER_NUMBER 4, 0, 0, 686 #define FB_MAJOR_VER "4" #define FB_MINOR_VER "0" #define FB_REV_NO "0" -#define FB_BUILD_NO "582" +#define FB_BUILD_NO "686" #define FB_BUILD_TYPE "T" -#define FB_BUILD_SUFFIX "Firebird 4.0 Unstable" +#define FB_BUILD_SUFFIX "Firebird 4.0 Alpha 1" diff --git a/src/jrd/cmp.cpp b/src/jrd/cmp.cpp index 5ead14d8ac..150d132453 100644 --- a/src/jrd/cmp.cpp +++ b/src/jrd/cmp.cpp @@ -120,7 +120,7 @@ ValueExprNode* CMP_clone_node_opt(thread_db* tdbb, CompilerScratch* csb, ValueEx DEV_BLKCHK(csb, type_csb); - if (node->is()) + if (nodeIs(node)) return node; SubExprNodeCopier copier(csb); diff --git a/src/jrd/constants.h b/src/jrd/constants.h index f070a8b1d9..ed6e38e3e2 100644 --- a/src/jrd/constants.h +++ b/src/jrd/constants.h @@ -451,6 +451,7 @@ const TraNumber MAX_TRA_NUMBER = 0x0000FFFFFFFFFFFF; // ~2.8 * 10^14 const int OPT_STATIC_ITEMS = 64; #define CURRENT_ENGINE "Engine13" +#define EMBEDDED_PROVIDERS "Providers=" CURRENT_ENGINE const int WITH_GRANT_OPTION = 1; const int WITH_ADMIN_OPTION = 2; diff --git a/src/jrd/cvt.cpp b/src/jrd/cvt.cpp index c0ffb2e122..aa9ad6374b 100644 --- a/src/jrd/cvt.cpp +++ b/src/jrd/cvt.cpp @@ -136,7 +136,7 @@ double CVT_date_to_double(const dsc* desc) temp_desc.dsc_length = sizeof(temp); date = temp; temp_desc.dsc_address = (UCHAR*) date; - CVT_move(desc, &temp_desc); + CVT_move(desc, &temp_desc, 0); } } @@ -173,7 +173,7 @@ void CVT_double_to_date(double real, SLONG fixed[2]) } -UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, double* ptr) +UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, void* ptr) { /************************************** * @@ -207,7 +207,7 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, d SINT64 value = 0; SSHORT local_scale = 0, sign = 0; - bool digit_seen = false, fraction = false; + bool digit_seen = false, fraction = false, over = false; const UCHAR* p = string; const UCHAR* const end = p + length; @@ -225,11 +225,11 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, d if (value >= NUMERIC_LIMIT) { // possibility of an overflow - if (value > NUMERIC_LIMIT) - break; - - if ((*p > '8' && sign == -1) || (*p > '7' && sign != -1)) + if ((value > NUMERIC_LIMIT) || (*p > '8' && sign == -1) || (*p > '7' && sign != -1)) + { + over = true; break; + } } // Force the subtraction to be performed before the addition, @@ -258,12 +258,23 @@ UCHAR CVT_get_numeric(const UCHAR* string, const USHORT length, SSHORT* scale, d if (!digit_seen) CVT_conversion_error(&desc, ERR_post); - if ((p < end) || // there is an exponent - ((value < 0) && (sign != -1))) // MAX_SINT64+1 wrapped around + if ((local_scale > MAX_SCHAR) || (local_scale < MIN_SCHAR)) + over = true; + + if ((!over) && ((p < end) || // there is an exponent + ((value < 0) && (sign != -1)))) // MAX_SINT64+1 wrapped around { // convert to double - *ptr = CVT_get_double(&desc, ERR_post); - return dtype_double; + *(double*) ptr = CVT_get_double(&desc, 0, ERR_post, &over); + if (!over) + return dtype_double; + } + + if (over) + { + thread_db* tdbb = JRD_get_thread_data(); + *(Decimal128*) ptr = CVT_get_dec128(&desc, tdbb->getAttachment()->att_dec_status, ERR_post); + return dtype_dec128; } *scale = local_scale; @@ -313,7 +324,7 @@ GDS_DATE CVT_get_sql_date(const dsc* desc) memset(&temp_desc, 0, sizeof(temp_desc)); temp_desc.dsc_dtype = dtype_sql_date; temp_desc.dsc_address = (UCHAR *) &value; - CVT_move(desc, &temp_desc); + CVT_move(desc, &temp_desc, 0); return value; } @@ -338,7 +349,7 @@ GDS_TIME CVT_get_sql_time(const dsc* desc) memset(&temp_desc, 0, sizeof(temp_desc)); temp_desc.dsc_dtype = dtype_sql_time; temp_desc.dsc_address = (UCHAR *) &value; - CVT_move(desc, &temp_desc); + CVT_move(desc, &temp_desc, 0); return value; } @@ -363,7 +374,7 @@ GDS_TIMESTAMP CVT_get_timestamp(const dsc* desc) memset(&temp_desc, 0, sizeof(temp_desc)); temp_desc.dsc_dtype = dtype_timestamp; temp_desc.dsc_address = (UCHAR *) &value; - CVT_move(desc, &temp_desc); + CVT_move(desc, &temp_desc, 0); return value; } diff --git a/src/jrd/cvt2.cpp b/src/jrd/cvt2.cpp index 06c1b842f1..7840fe7f7b 100644 --- a/src/jrd/cvt2.cpp +++ b/src/jrd/cvt2.cpp @@ -65,11 +65,10 @@ using namespace Firebird; comparison purpose, even though isc_int64 had to get number 19, which is otherwise too high. - This table is used in CVT2_compare, is indexed by dsc_dtype, and - returns the relative priority of types for use when different types - are compared. + This table is indexed by dsc_dtype, and returns the relative priority + of types for use when different types are compared. */ -static const BYTE compare_priority[] = +const BYTE CVT2_compare_priority[] = { dtype_unknown, // dtype_unknown through dtype_varying dtype_text, // have their natural values stored @@ -80,18 +79,20 @@ static const BYTE compare_priority[] = dtype_byte, // their natural values in the table dtype_short, dtype_long, - dtype_quad + 1, // quad through array all move up - dtype_real + 1, // by one to make room for int64 - dtype_double + 1, // at its proper place in the table. - dtype_d_float + 1, - dtype_sql_date + 1, - dtype_sql_time + 1, - dtype_timestamp + 1, - dtype_blob + 1, - dtype_array + 1, + dtype_quad + 1, // Move quad up by one to make room for int64 at its proper place in the table. + dtype_real + 3, // Also leave space for dec64 and dec 128. + dtype_double + 3, + dtype_d_float + 3, + dtype_sql_date + 3, + dtype_sql_time + 3, + dtype_timestamp + 3, + dtype_blob + 3, + dtype_array + 3, dtype_long + 1, // int64 goes right after long dtype_dbkey, // compares with nothing except itself - dtype_boolean // compares with nothing except itself + dtype_boolean, // compares with nothing except itself + dtype_quad + 2, // dec64 and dec128 go after quad before real + dtype_quad + 3 }; static inline int QUAD_COMPARE(const SQUAD* arg1, const SQUAD* arg2) @@ -177,7 +178,7 @@ bool CVT2_get_binary_comparable_desc(dsc* result, const dsc* arg1, const dsc* ar else { // Arguments are of different data types - *result = (compare_priority[arg1->dsc_dtype] > compare_priority[arg2->dsc_dtype]) ? *arg1 : *arg2; + *result = (CVT2_compare_priority[arg1->dsc_dtype] > CVT2_compare_priority[arg2->dsc_dtype]) ? *arg1 : *arg2; if (arg1->isExact() && arg2->isExact()) result->dsc_scale = MIN(arg1->dsc_scale, arg2->dsc_scale); @@ -187,7 +188,7 @@ bool CVT2_get_binary_comparable_desc(dsc* result, const dsc* arg1, const dsc* ar } -int CVT2_compare(const dsc* arg1, const dsc* arg2) +int CVT2_compare(const dsc* arg1, const dsc* arg2, Firebird::DecimalStatus decSt) { /************************************** * @@ -285,6 +286,12 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) return 1; return -1; + case dtype_dec64: + return ((Decimal64*) p1)->compare(decSt, *(Decimal64*) p2); + + case dtype_dec128: + return ((Decimal128*) p1)->compare(decSt, *(Decimal128*) p2); + case dtype_boolean: return *p1 == *p2 ? 0 : *p1 < *p2 ? -1 : 1; @@ -342,8 +349,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) UCHAR* p1 = NULL; UCHAR* p2 = NULL; USHORT t1, t2; // unused later - USHORT length = CVT_get_string_ptr(arg1, &t1, &p1, NULL, 0); - USHORT length2 = CVT_get_string_ptr(arg2, &t2, &p2, NULL, 0); + USHORT length = CVT_get_string_ptr(arg1, &t1, &p1, NULL, 0, decSt); + USHORT length2 = CVT_get_string_ptr(arg2, &t2, &p2, NULL, 0, decSt); int fill = length - length2; const UCHAR pad = charset1 == ttype_binary || charset2 == ttype_binary ? '\0' : ' '; @@ -391,8 +398,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) // Handle heterogeneous compares - if (compare_priority[arg1->dsc_dtype] < compare_priority[arg2->dsc_dtype]) - return -CVT2_compare(arg2, arg1); + if (CVT2_compare_priority[arg1->dsc_dtype] < CVT2_compare_priority[arg2->dsc_dtype]) + return -CVT2_compare(arg2, arg1, decSt); // At this point, the type of arg1 is guaranteed to be "greater than" arg2, // in the sense that it is the preferred type for comparing the two. @@ -407,8 +414,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) SLONG datetime[2]; desc.dsc_length = sizeof(datetime); desc.dsc_address = (UCHAR*) datetime; - CVT_move(arg2, &desc); - return CVT2_compare(arg1, &desc); + CVT_move(arg2, &desc, 0); + return CVT2_compare(arg1, &desc, 0); } case dtype_sql_time: @@ -419,8 +426,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) SLONG atime; desc.dsc_length = sizeof(atime); desc.dsc_address = (UCHAR*) &atime; - CVT_move(arg2, &desc); - return CVT2_compare(arg1, &desc); + CVT_move(arg2, &desc, 0); + return CVT2_compare(arg1, &desc, 0); } case dtype_sql_date: @@ -431,8 +438,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) SLONG date; desc.dsc_length = sizeof(date); desc.dsc_address = (UCHAR*) &date; - CVT_move(arg2, &desc); - return CVT2_compare(arg1, &desc); + CVT_move(arg2, &desc, 0); + return CVT2_compare(arg1, &desc, 0); } case dtype_short: @@ -442,8 +449,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) scale = MIN(arg1->dsc_scale, arg2->dsc_scale); else scale = arg1->dsc_scale; - const SLONG temp1 = CVT_get_long(arg1, scale, ERR_post); - const SLONG temp2 = CVT_get_long(arg2, scale, ERR_post); + const SLONG temp1 = CVT_get_long(arg1, scale, decSt, ERR_post); + const SLONG temp2 = CVT_get_long(arg2, scale, decSt, ERR_post); if (temp1 == temp2) return 0; if (temp1 > temp2) @@ -460,8 +467,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) scale = MIN(arg1->dsc_scale, arg2->dsc_scale); else scale = arg1->dsc_scale; - const SINT64 temp1 = CVT_get_int64(arg1, scale, ERR_post); - const SINT64 temp2 = CVT_get_int64(arg2, scale, ERR_post); + const SINT64 temp1 = CVT_get_int64(arg1, scale, decSt, ERR_post); + const SINT64 temp2 = CVT_get_int64(arg2, scale, decSt, ERR_post); if (temp1 == temp2) return 0; if (temp1 > temp2) @@ -476,15 +483,15 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) scale = MIN(arg1->dsc_scale, arg2->dsc_scale); else scale = arg1->dsc_scale; - const SQUAD temp1 = CVT_get_quad(arg1, scale, ERR_post); - const SQUAD temp2 = CVT_get_quad(arg2, scale, ERR_post); + const SQUAD temp1 = CVT_get_quad(arg1, scale, decSt, ERR_post); + const SQUAD temp2 = CVT_get_quad(arg2, scale, decSt, ERR_post); return QUAD_COMPARE(&temp1, &temp2); } case dtype_real: { - const float temp1 = (float) CVT_get_double(arg1, ERR_post); - const float temp2 = (float) CVT_get_double(arg2, ERR_post); + const float temp1 = (float) CVT_get_double(arg1, decSt, ERR_post); + const float temp2 = (float) CVT_get_double(arg2, decSt, ERR_post); if (temp1 == temp2) return 0; if (temp1 > temp2) @@ -494,8 +501,8 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) case dtype_double: { - const double temp1 = CVT_get_double(arg1, ERR_post); - const double temp2 = CVT_get_double(arg2, ERR_post); + const double temp1 = CVT_get_double(arg1, decSt, ERR_post); + const double temp2 = CVT_get_double(arg2, decSt, ERR_post); if (temp1 == temp2) return 0; if (temp1 > temp2) @@ -503,8 +510,22 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) return -1; } + case dtype_dec64: + { + const Decimal64 temp1 = CVT_get_dec64(arg1, decSt, ERR_post); + const Decimal64 temp2 = CVT_get_dec64(arg2, decSt, ERR_post); + return temp1.compare(decSt, temp2); + } + + case dtype_dec128: + { + const Decimal128 temp1 = CVT_get_dec128(arg1, decSt, ERR_post); + const Decimal128 temp2 = CVT_get_dec128(arg2, decSt, ERR_post); + return temp1.compare(decSt, temp2); + } + case dtype_blob: - return CVT2_blob_compare(arg1, arg2); + return CVT2_blob_compare(arg1, arg2, decSt); case dtype_array: ERR_post(Arg::Gds(isc_wish_list) << Arg::Gds(isc_blobnotsup) << "compare"); @@ -515,7 +536,7 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) { UCHAR* p = NULL; USHORT t; // unused later - USHORT length = CVT_get_string_ptr(arg2, &t, &p, NULL, 0); + USHORT length = CVT_get_string_ptr(arg2, &t, &p, NULL, 0, decSt); USHORT l = MIN(arg1->dsc_length, length); int rc = memcmp(arg1->dsc_address, p, l); @@ -548,7 +569,7 @@ int CVT2_compare(const dsc* arg1, const dsc* arg2) } -int CVT2_blob_compare(const dsc* arg1, const dsc* arg2) +int CVT2_blob_compare(const dsc* arg1, const dsc* arg2, DecimalStatus decSt) { /************************************** * @@ -709,7 +730,7 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2) UCHAR* p; MoveBuffer temp_str; - l2 = CVT2_make_string2(arg2, ttype1, &p, temp_str); + l2 = CVT2_make_string2(arg2, ttype1, &p, temp_str, decSt); blb* blob1 = blb::open(tdbb, tdbb->getRequest()->req_transaction, (bid*) arg1->dsc_address); @@ -734,7 +755,7 @@ int CVT2_blob_compare(const dsc* arg1, const dsc* arg2) } -USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, Jrd::MoveBuffer& temp) +USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, MoveBuffer& temp, DecimalStatus decSt) { /************************************** * @@ -813,7 +834,7 @@ USHORT CVT2_make_string2(const dsc* desc, USHORT to_interp, UCHAR** address, Jrd vary* vtmp = reinterpret_cast(temp_desc.dsc_address); temp_desc.dsc_dtype = dtype_varying; temp_desc.setTextType(to_interp); - CVT_move(desc, &temp_desc); + CVT_move(desc, &temp_desc, decSt); *address = reinterpret_cast(vtmp->vary_string); return vtmp->vary_length; diff --git a/src/jrd/cvt2_proto.h b/src/jrd/cvt2_proto.h index 95b3299065..c04f62439c 100644 --- a/src/jrd/cvt2_proto.h +++ b/src/jrd/cvt2_proto.h @@ -26,9 +26,11 @@ #include "../jrd/jrd.h" +extern const BYTE CVT2_compare_priority[]; + bool CVT2_get_binary_comparable_desc(dsc*, const dsc*, const dsc*); -int CVT2_compare(const dsc*, const dsc*); -int CVT2_blob_compare(const dsc*, const dsc*); -USHORT CVT2_make_string2(const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&); +int CVT2_compare(const dsc*, const dsc*, Firebird::DecimalStatus); +int CVT2_blob_compare(const dsc*, const dsc*, Firebird::DecimalStatus); +USHORT CVT2_make_string2(const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, Firebird::DecimalStatus); #endif // JRD_CVT2_PROTO_H diff --git a/src/jrd/cvt_proto.h b/src/jrd/cvt_proto.h index e103910b12..2fee524a09 100644 --- a/src/jrd/cvt_proto.h +++ b/src/jrd/cvt_proto.h @@ -30,7 +30,7 @@ double CVT_date_to_double(const dsc*); void CVT_double_to_date(double, SLONG[2]); -UCHAR CVT_get_numeric(const UCHAR*, const USHORT, SSHORT*, double*); +UCHAR CVT_get_numeric(const UCHAR*, const USHORT, SSHORT*, void*); GDS_DATE CVT_get_sql_date(const dsc*); GDS_TIME CVT_get_sql_time(const dsc*); GDS_TIMESTAMP CVT_get_timestamp(const dsc*); @@ -65,15 +65,15 @@ namespace Jrd }; } -inline void CVT_move(const dsc* from, dsc* to) +inline void CVT_move(const dsc* from, dsc* to, Firebird::DecimalStatus decSt) { - CVT_move_common(from, to, &Jrd::EngineCallbacks::instance); + CVT_move_common(from, to, decSt, &Jrd::EngineCallbacks::instance); } inline USHORT CVT_get_string_ptr(const dsc* desc, USHORT* ttype, UCHAR** address, - vary* temp, USHORT length) + vary* temp, USHORT length, Firebird::DecimalStatus decSt) { - return CVT_get_string_ptr_common(desc, ttype, address, temp, length, + return CVT_get_string_ptr_common(desc, ttype, address, temp, length, decSt, &Jrd::EngineCallbacks::instance); } diff --git a/src/jrd/dfw.epp b/src/jrd/dfw.epp index e9ffc856cd..90b3d16123 100644 --- a/src/jrd/dfw.epp +++ b/src/jrd/dfw.epp @@ -1241,6 +1241,9 @@ USHORT DFW_assign_index_type(thread_db* tdbb, const Firebird::MetaName& name, SS return idx_numeric2; case dtype_boolean: return idx_boolean; + case dtype_dec64: + case dtype_dec128: + return idx_decimal; default: return idx_numeric; } @@ -1274,7 +1277,7 @@ void DFW_delete_deferred( jrd_tra* transaction, SavNumber sav_number) if (sav_number == -1) { DeferredWork* work; - while (work = transaction->tra_deferred_job->work) + while ((work = transaction->tra_deferred_job->work)) { delete work; } @@ -2264,7 +2267,7 @@ static bool clear_cache(thread_db* tdbb, SSHORT phase, DeferredWork* work, jrd_t return true; case 3: - clearMappingCache(dbb->dbb_filename.c_str(), work->dfw_id); + Mapping::clearCache(dbb->dbb_filename.c_str(), work->dfw_id); break; } @@ -3714,13 +3717,14 @@ static bool create_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jr // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) { - MET_load_trigger(tdbb, NULL, work->dfw_name, - &tdbb->getAttachment()->att_triggers[arg->dfw_id & ~TRIGGER_TYPE_DB]); + unsigned triggerKind = arg->dfw_id & ~TRIGGER_TYPE_DB; + MET_release_triggers(tdbb, &tdbb->getAttachment()->att_triggers[triggerKind]); + MET_load_db_triggers(tdbb, triggerKind); } else if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) { - MET_load_trigger(tdbb, NULL, work->dfw_name, - &tdbb->getAttachment()->att_ddl_triggers); + MET_release_triggers(tdbb, &tdbb->getAttachment()->att_ddl_triggers); + MET_load_ddl_triggers(tdbb); } } } @@ -6017,12 +6021,14 @@ static bool modify_trigger(thread_db* tdbb, SSHORT phase, DeferredWork* work, jr // ASF: arg->dfw_id is RDB$TRIGGER_TYPE truncated to USHORT if (arg && (arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DB) { - MET_release_trigger(tdbb, - &tdbb->getAttachment()->att_triggers[arg->dfw_id & ~TRIGGER_TYPE_DB], - work->dfw_name); - - MET_load_trigger(tdbb, NULL, work->dfw_name, - &tdbb->getAttachment()->att_triggers[arg->dfw_id & ~TRIGGER_TYPE_DB]); + unsigned triggerKind = arg->dfw_id & ~TRIGGER_TYPE_DB; + MET_release_triggers(tdbb, &tdbb->getAttachment()->att_triggers[triggerKind]); + MET_load_db_triggers(tdbb, triggerKind); + } + else if ((arg->dfw_id & TRIGGER_TYPE_MASK) == TRIGGER_TYPE_DDL) + { + MET_release_triggers(tdbb, &tdbb->getAttachment()->att_ddl_triggers); + MET_load_ddl_triggers(tdbb); } } } @@ -6452,7 +6458,7 @@ static string get_string(const dsc* desc) // Find the actual length of the string, searching until the claimed // end of the string, or the terminating \0, whichever comes first. - USHORT length = MOV_make_string(desc, ttype_metadata, &str, &temp, sizeof(temp)); + USHORT length = MOV_make_string(JRD_get_thread_data(), desc, ttype_metadata, &str, &temp, sizeof(temp)); const char* p = str; const char* const q = str + length; diff --git a/src/jrd/err.cpp b/src/jrd/err.cpp index 0f428ac2d4..4bea1b95ff 100644 --- a/src/jrd/err.cpp +++ b/src/jrd/err.cpp @@ -102,6 +102,26 @@ void ERR_bugcheck_msg(const TEXT* msg) } +void ERR_soft_bugcheck(int number, const TEXT* file, int line) +{ +/************************************** + * + * E R R _ s o f t _ b u g c h e c k + * + ************************************** + * + * Functional description + * Things seem to be going poorly today. + * Use this function instead of ERR_bugcheck in bugcheck conditions that do not affect + * whole server/database/attachment state. + * + **************************************/ + + fb_assert(false); + internal_error(isc_bug_check, number, file, line); +} + + void ERR_corrupt(int number) { /************************************** diff --git a/src/jrd/err_proto.h b/src/jrd/err_proto.h index c5964b6592..f6d28a6ede 100644 --- a/src/jrd/err_proto.h +++ b/src/jrd/err_proto.h @@ -50,6 +50,7 @@ void ERR_post_warning(const Firebird::Arg::StatusVector& v); void ERR_assert(const TEXT*, int); void ERR_bugcheck(int, const TEXT* = NULL, int = 0); void ERR_bugcheck_msg(const TEXT*); +void ERR_soft_bugcheck(int, const TEXT*, int); void ERR_corrupt(int); void ERR_error(int); void ERR_post(const Firebird::Arg::StatusVector& v); diff --git a/src/jrd/evl.cpp b/src/jrd/evl.cpp index 3f9f599193..99ea09aed4 100644 --- a/src/jrd/evl.cpp +++ b/src/jrd/evl.cpp @@ -148,7 +148,7 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) const VariableNode* varNode; const FieldNode* fieldNode; - if ((paramNode = ExprNode::as(node))) + if ((paramNode = nodeAs(node))) { message = paramNode->message; arg_number = paramNode->argNumber; @@ -175,15 +175,15 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) return &impure->vlu_desc; } - else if (ExprNode::is(node)) + else if (nodeIs(node)) return NULL; - else if ((varNode = ExprNode::as(node))) + else if ((varNode = nodeAs(node))) { // Calculate descriptor impure = request->getImpure(varNode->varDecl->impureOffset); return &impure->vlu_desc; } - else if ((fieldNode = ExprNode::as(node))) + else if ((fieldNode = nodeAs(node))) { record = request->req_rpb[fieldNode->fieldStream].rpb_record; @@ -202,7 +202,7 @@ dsc* EVL_assign_to(thread_db* tdbb, const ValueExprNode* node) return &impure->vlu_desc; } - BUGCHECK(229); // msg 229 EVL_assign_to: invalid operation + SOFT_BUGCHECK(229); // msg 229 EVL_assign_to: invalid operation return NULL; } @@ -261,7 +261,7 @@ RecordBitmap** EVL_bitmap(thread_db* tdbb, const InversionNode* node, RecordBitm (desc->isText() || desc->isDbKey())) { UCHAR* ptr = NULL; - const int length = MOV_get_string(desc, &ptr, NULL, 0); + const int length = MOV_get_string(tdbb, desc, &ptr, NULL, 0); if (length == sizeof(RecordNumber::Packed)) { @@ -290,7 +290,7 @@ RecordBitmap** EVL_bitmap(thread_db* tdbb, const InversionNode* node, RecordBitm } default: - BUGCHECK(230); // msg 230 EVL_bitmap: invalid operation + SOFT_BUGCHECK(230); // msg 230 EVL_bitmap: invalid operation } return NULL; @@ -426,6 +426,14 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor value->vlu_misc.vlu_double = *((double*) from.dsc_address); return; + case dtype_dec64: + value->vlu_misc.vlu_dec64 = *((Decimal64*) from.dsc_address); + return; + + case dtype_dec128: + value->vlu_misc.vlu_dec128 = *((Decimal128*) from.dsc_address); + return; + case dtype_timestamp: case dtype_quad: value->vlu_misc.vlu_dbkey[0] = ((SLONG*) from.dsc_address)[0]; @@ -459,7 +467,7 @@ void EVL_make_value(thread_db* tdbb, const dsc* desc, impure_value* value, Memor // temporary buffer. Since this will always be the result of a conversion, // this isn't a serious problem. - const USHORT length = MOV_get_string_ptr(&from, &ttype, &address, &temp, sizeof(temp)); + const USHORT length = MOV_get_string_ptr(tdbb, &from, &ttype, &address, &temp, sizeof(temp)); // Allocate a string block of sufficient size. @@ -539,7 +547,7 @@ void EVL_validate(thread_db* tdbb, const Item& item, const ItemInfo* itemInfo, d if (!fieldInfo.validationExpr->execute(tdbb, request) && !(request->req_flags & req_null)) { const USHORT length = desc_is_null ? 0 : - MOV_make_string(desc, ttype_dynamic, &value, &temp, sizeof(temp) - 1); + MOV_make_string(tdbb, desc, ttype_dynamic, &value, &temp, sizeof(temp) - 1); if (desc_is_null) value = NULL_STRING_MARK; diff --git a/src/jrd/exe.cpp b/src/jrd/exe.cpp index 910287d837..0c64c4d1f2 100644 --- a/src/jrd/exe.cpp +++ b/src/jrd/exe.cpp @@ -280,14 +280,14 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo SSHORT null = from_null ? -1 : 0; - if (!null && missing && MOV_compare(missing, from_desc) == 0) + if (!null && missing && MOV_compare(tdbb, missing, from_desc) == 0) null = -1; USHORT* impure_flags = NULL; const ParameterNode* toParam; const VariableNode* toVar; - if ((toParam = ExprNode::as(to))) + if ((toParam = nodeAs(to))) { const MessageNode* message = toParam->message; @@ -300,7 +300,7 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo impure_flags = request->getImpure( message->impureFlags + (sizeof(USHORT) * toParam->argNumber)); } - else if ((toVar = ExprNode::as(to))) + else if ((toVar = nodeAs(to))) { if (toVar->varInfo) { @@ -432,7 +432,7 @@ void EXE_assignment(thread_db* tdbb, const ValueExprNode* to, dsc* from_desc, bo // Handle the null flag as appropriate for fields and message arguments. - const FieldNode* toField = ExprNode::as(to); + const FieldNode* toField = nodeAs(to); if (toField) { Record* record = request->req_rpb[toField->fieldStream].rpb_record; @@ -793,10 +793,10 @@ void EXE_send(thread_db* tdbb, jrd_req* request, USHORT msg, ULONG length, const for (const NestConst* end = selectNode->statements.end(); ptr != end; ++ptr) { - const ReceiveNode* receiveNode = (*ptr)->as(); + const ReceiveNode* receiveNode = nodeAs(*ptr); message = receiveNode->message; - if (message->as()->messageNumber == msg) + if (nodeAs(message)->messageNumber == msg) { request->req_next = *ptr; break; @@ -911,7 +911,7 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) { const JrdStatement* statement = request->getStatement(); - if (statement->fors.getCount() || request->req_ext_stmt) + if (statement->fors.getCount() || request->req_ext_resultset || request->req_ext_stmt) { Jrd::ContextPoolHolder context(tdbb, request->req_pool); jrd_req* old_request = tdbb->getRequest(); @@ -927,7 +927,10 @@ void EXE_unwind(thread_db* tdbb, jrd_req* request) } if (request->req_ext_resultset) + { delete request->req_ext_resultset; + request->req_ext_resultset = NULL; + } while (request->req_ext_stmt) request->req_ext_stmt->close(tdbb); diff --git a/src/jrd/ext.cpp b/src/jrd/ext.cpp index dae4dfe752..bea2873a19 100644 --- a/src/jrd/ext.cpp +++ b/src/jrd/ext.cpp @@ -346,7 +346,7 @@ void EXT_fini(jrd_rel* relation, bool close_only) } -bool EXT_get(thread_db* /*tdbb*/, record_param* rpb, FB_UINT64& position) +bool EXT_get(thread_db* tdbb, record_param* rpb, FB_UINT64& position) { /************************************** * @@ -432,14 +432,14 @@ bool EXT_get(thread_db* /*tdbb*/, record_param* rpb, FB_UINT64& position) if (!desc_ptr->dsc_length || !field) continue; - const LiteralNode* literal = ExprNode::as(field->fld_missing_value); + const LiteralNode* literal = nodeAs(field->fld_missing_value); if (literal) { desc = *desc_ptr; desc.dsc_address = record->getData() + (IPTR) desc.dsc_address; - if (!MOV_compare(&literal->litDesc, &desc)) + if (!MOV_compare(tdbb, &literal->litDesc, &desc)) continue; } @@ -534,7 +534,7 @@ void EXT_store(thread_db* tdbb, record_param* rpb) if (field && !field->fld_computation && desc_ptr->dsc_length && record->isNull(i)) { UCHAR* p = record->getData() + (IPTR) desc_ptr->dsc_address; - LiteralNode* literal = ExprNode::as(field->fld_missing_value); + LiteralNode* literal = nodeAs(field->fld_missing_value); if (literal) { diff --git a/src/jrd/extds/InternalDS.cpp b/src/jrd/extds/InternalDS.cpp index e868604f17..0f2791086c 100644 --- a/src/jrd/extds/InternalDS.cpp +++ b/src/jrd/extds/InternalDS.cpp @@ -285,7 +285,7 @@ void InternalTransaction::doStart(FbStatusVector* status, thread_db* tdbb, Clump fb_assert(localTran); if (m_scope == traCommon && m_IntConnection.isCurrent()) - m_transaction = localTran->getInterface(); + m_transaction = localTran->getInterface(true); else { JAttachment* att = m_IntConnection.getJrdAtt(); diff --git a/src/jrd/extds/IscDS.cpp b/src/jrd/extds/IscDS.cpp index b11eb163ab..84152b9cbc 100644 --- a/src/jrd/extds/IscDS.cpp +++ b/src/jrd/extds/IscDS.cpp @@ -63,7 +63,6 @@ static RegisterFBProvider reg; static bool isConnectionBrokenError(FbStatusVector* status); static void parseSQLDA(XSQLDA* xsqlda, UCharBuffer& buff, Firebird::Array& descs); -static UCHAR sqlTypeToDscType(SSHORT sqlType); // IscProvider @@ -1658,7 +1657,7 @@ static void parseSQLDA(XSQLDA* xsqlda, UCharBuffer& buff, Firebird::Array & for (int i = 0; i < xsqlda->sqld; xVar++, i++) { - const UCHAR dtype = sqlTypeToDscType(xVar->sqltype & ~1); + const UCHAR dtype = fb_utils::sqlTypeToDscType(xVar->sqltype & ~1); xVar->sqltype |= 1; if (type_alignments[dtype]) @@ -1683,7 +1682,7 @@ static void parseSQLDA(XSQLDA* xsqlda, UCharBuffer& buff, Firebird::Array & for (int i = 0; i < xsqlda->sqld; xVar++, i++) { - const UCHAR dtype = sqlTypeToDscType(xVar->sqltype & ~1); + const UCHAR dtype = fb_utils::sqlTypeToDscType(xVar->sqltype & ~1); if (type_alignments[dtype]) offset = FB_ALIGN(offset, type_alignments[dtype]); @@ -1720,47 +1719,5 @@ static void parseSQLDA(XSQLDA* xsqlda, UCharBuffer& buff, Firebird::Array & } -static UCHAR sqlTypeToDscType(SSHORT sqlType) -{ - switch (sqlType) - { - case SQL_VARYING: - return dtype_varying; - case SQL_TEXT: - return dtype_text; - case SQL_NULL: - return dtype_text; - case SQL_DOUBLE: - return dtype_double; - case SQL_FLOAT: - return dtype_real; - case SQL_D_FLOAT: - return dtype_d_float; - case SQL_TYPE_DATE: - return dtype_sql_date; - case SQL_TYPE_TIME: - return dtype_sql_time; - case SQL_TIMESTAMP: - return dtype_timestamp; - case SQL_BLOB: - return dtype_blob; - case SQL_ARRAY: - return dtype_array; - case SQL_LONG: - return dtype_long; - case SQL_SHORT: - return dtype_short; - case SQL_INT64: - return dtype_int64; - case SQL_QUAD: - return dtype_quad; - case SQL_BOOLEAN: - return dtype_boolean; - default: - return dtype_unknown; - } -} - - } // namespace EDS diff --git a/src/jrd/flu.cpp b/src/jrd/flu.cpp index 9c15e47090..60d5d7e735 100644 --- a/src/jrd/flu.cpp +++ b/src/jrd/flu.cpp @@ -268,11 +268,20 @@ namespace Jrd return Module(); } + Module::~Module() + { + if (interMod) + { + Firebird::MutexLockGuard lg(modulesMutex, FB_FUNCTION); + interMod = NULL; // This makes RefPtr call release() + } + } + Module::InternalModule::~InternalModule() { - delete handle; + fb_assert(modulesMutex->locked()); - Firebird::MutexLockGuard lg(modulesMutex, FB_FUNCTION); + delete handle; for (FB_SIZE_T m = 0; m < loadedModules().getCount(); m++) { diff --git a/src/jrd/flu.h b/src/jrd/flu.h index 3dd8fc0dbc..9faabd0926 100644 --- a/src/jrd/flu.h +++ b/src/jrd/flu.h @@ -102,6 +102,8 @@ namespace Jrd : interMod(m.interMod) { } + ~Module(); + // used for UDF/BLOB Filter static FPTR_INT lookup(const char*, const char*, Firebird::SortedObjectsArray&); diff --git a/src/jrd/fun.epp b/src/jrd/fun.epp index 2efedbacb1..13521232ec 100644 --- a/src/jrd/fun.epp +++ b/src/jrd/fun.epp @@ -486,7 +486,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra { case dtype_short: { - const SSHORT s = MOV_get_long(input, (SSHORT) parameter->prm_desc.dsc_scale); + const SSHORT s = MOV_get_long(tdbb, input, (SSHORT) parameter->prm_desc.dsc_scale); if (parameter->prm_fun_mechanism == FUN_value) { // For (apparent) portability reasons, SHORT by value @@ -505,7 +505,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra break; case dtype_long: - l = MOV_get_long(input, (SSHORT) parameter->prm_desc.dsc_scale); + l = MOV_get_long(tdbb, input, (SSHORT) parameter->prm_desc.dsc_scale); if (parameter->prm_fun_mechanism == FUN_value) { *arg_ptr++ = (UDF_ARG)(IPTR) l; @@ -540,7 +540,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra case dtype_int64: { SINT64* pi64; - const SINT64 i64 = MOV_get_int64(input, (SSHORT) parameter->prm_desc.dsc_scale); + const SINT64 i64 = MOV_get_int64(tdbb, input, (SSHORT) parameter->prm_desc.dsc_scale); if (parameter->prm_fun_mechanism == FUN_value) { @@ -556,7 +556,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra case dtype_real: { - const float f = (float) MOV_get_double(input); + const float f = (float) MOV_get_double(tdbb, input); if (parameter->prm_fun_mechanism == FUN_value) { // For (apparent) portability reasons, FLOAT by value @@ -576,7 +576,7 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra break; case dtype_double: - d = MOV_get_double(input); + d = MOV_get_double(tdbb, input); double* dp; if (parameter->prm_fun_mechanism == FUN_value) { @@ -589,6 +589,36 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra *dp = d; break; + case dtype_dec64: + { + const Decimal64 d = MOV_get_dec64(tdbb, input); + if (parameter->prm_fun_mechanism == FUN_value) + { + Decimal64* dp = reinterpret_cast(arg_ptr); + *dp++ = d; + arg_ptr = reinterpret_cast(dp); + continue; + } + Decimal64* fp = (Decimal64*) temp_ptr; + *fp = d; + } + break; + + case dtype_dec128: + { + const Decimal128 d = MOV_get_dec128(tdbb, input); + if (parameter->prm_fun_mechanism == FUN_value) + { + Decimal128* dp = reinterpret_cast(arg_ptr); + *dp++ = d; + arg_ptr = reinterpret_cast(dp); + continue; + } + Decimal128* fp = (Decimal128*) temp_ptr; + *fp = d; + } + break; + case dtype_text: case dtype_dbkey: case dtype_cstring: @@ -742,8 +772,9 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra } else { - if (value->vlu_desc.dsc_dtype == dtype_double) + switch (value->vlu_desc.dsc_dtype) { + case dtype_double: if (isinf(value->vlu_misc.vlu_double)) { status_exception::raise(Arg::Gds(isc_expression_eval_err) << @@ -756,14 +787,45 @@ void FUN_evaluate(thread_db* tdbb, const Function* function, const NestValueArra Arg::Gds(isc_udf_fp_nan) << Arg::Str(function->getName().toString())); } + break; + + case dtype_dec64: + if (value->vlu_misc.vlu_dec64.isInf()) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_udf_fp_overflow) << + Arg::Str(function->getName().toString())); + } + else if (value->vlu_misc.vlu_dec64.isNan()) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_udf_fp_nan) << + Arg::Str(function->getName().toString())); + } + break; + + case dtype_dec128: + if (value->vlu_misc.vlu_dec128.isInf()) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_udf_fp_overflow) << + Arg::Str(function->getName().toString())); + } + else if (value->vlu_misc.vlu_dec128.isNan()) + { + status_exception::raise(Arg::Gds(isc_expression_eval_err) << + Arg::Gds(isc_udf_fp_nan) << + Arg::Str(function->getName().toString())); + } + break; } + request->req_flags &= ~req_null; } while (array_stack.hasData()) - { delete[] array_stack.pop(); - } + blob_stack.close(); } // try @@ -1050,6 +1112,14 @@ static void invoke(thread_db* tdbb, value->vlu_misc.vlu_double = CALL_UDF(tdbb, function->fun_entrypoint, args); break; + case dtype_dec64: + value->vlu_misc.vlu_dec64 = CALL_UDF(tdbb, function->fun_entrypoint, args); + break; + + case dtype_dec128: + value->vlu_misc.vlu_dec128 = CALL_UDF(tdbb, function->fun_entrypoint, args); + break; + case dtype_timestamp: default: udfError = UeUnsupDtype; diff --git a/src/jrd/idx.cpp b/src/jrd/idx.cpp index ee5a32e159..1418e7ea61 100644 --- a/src/jrd/idx.cpp +++ b/src/jrd/idx.cpp @@ -314,16 +314,14 @@ void IDX_create_index(thread_db* tdbb, sort_key_def key_desc[2]; // Key sort description - key_desc[0].skd_dtype = SKD_bytes; + key_desc[0].setSkdLength(SKD_bytes, key_length); key_desc[0].skd_flags = SKD_ascending; - key_desc[0].skd_length = key_length; - key_desc[0].skd_offset = 0; + key_desc[0].setSkdOffset(); key_desc[0].skd_vary_offset = 0; // RecordNumber sort description - key_desc[1].skd_dtype = SKD_int64; + key_desc[1].setSkdLength(SKD_int64, sizeof(RecordNumber)); key_desc[1].skd_flags = SKD_ascending; - key_desc[1].skd_length = sizeof(RecordNumber); - key_desc[1].skd_offset = key_length; + key_desc[1].setSkdOffset(key_desc); key_desc[1].skd_vary_offset = 0; FPTR_REJECT_DUP_CALLBACK callback = (idx->idx_flags & idx_unique) ? duplicate_key : NULL; @@ -1110,7 +1108,7 @@ static idx_e check_duplicates(thread_db* tdbb, bool flag_rec = false; const dsc* desc_rec = BTR_eval_expression(tdbb, insertion_idx, rpb.rpb_record, flag_rec); - if (flag_rec && flag_idx && (MOV_compare(desc_rec, &desc1) == 0)) + if (flag_rec && flag_idx && (MOV_compare(tdbb, desc_rec, &desc1) == 0)) { result = idx_e_duplicate; break; @@ -1131,7 +1129,7 @@ static idx_e check_duplicates(thread_db* tdbb, field_id = record_idx->idx_rpt[i].idx_field; const bool flag_idx = EVL_field(relation_2, record, field_id, &desc2); - if (flag_rec != flag_idx || (flag_rec && (MOV_compare(&desc1, &desc2) != 0) )) + if (flag_rec != flag_idx || (flag_rec && (MOV_compare(tdbb, &desc1, &desc2) != 0) )) break; all_nulls = all_nulls && !flag_rec && !flag_idx; @@ -1564,7 +1562,7 @@ void IDX_modify_flag_uk_modified(thread_db* tdbb, const bool flag_org = EVL_field(org_rpb->rpb_relation, org_rpb->rpb_record, idx_desc->idx_field, &desc1); const bool flag_new = EVL_field(new_rpb->rpb_relation, new_rpb->rpb_record, idx_desc->idx_field, &desc2); - if (flag_org != flag_new || MOV_compare(&desc1, &desc2) != 0) + if (flag_org != flag_new || MOV_compare(tdbb, &desc1, &desc2) != 0) { new_rpb->rpb_flags |= rpb_uk_modified; CCH_RELEASE(tdbb, &window); diff --git a/src/jrd/inf.cpp b/src/jrd/inf.cpp index 66e3c97636..2373c90042 100644 --- a/src/jrd/inf.cpp +++ b/src/jrd/inf.cpp @@ -936,7 +936,7 @@ ULONG INF_request_info(const jrd_req* request, const ULONG item_length, const UC { const StmtNode* node = request->req_next; - if (node->is()) + if (nodeIs(node)) state = isc_info_req_select; else state = isc_info_req_receive; diff --git a/src/jrd/intl.cpp b/src/jrd/intl.cpp index bab0949ef2..a81c402b3d 100644 --- a/src/jrd/intl.cpp +++ b/src/jrd/intl.cpp @@ -609,11 +609,11 @@ int INTL_compare(thread_db* tdbb, const dsc* pText1, const dsc* pText2, ErrorFun UCHAR* p1; USHORT t1; - ULONG length1 = CVT_get_string_ptr(pText1, &t1, &p1, NULL, 0, err); + ULONG length1 = CVT_get_string_ptr(pText1, &t1, &p1, NULL, 0, tdbb->getAttachment()->att_dec_status, err); UCHAR* p2; USHORT t2; - ULONG length2 = CVT_get_string_ptr(pText2, &t2, &p2, NULL, 0, err); + ULONG length2 = CVT_get_string_ptr(pText2, &t2, &p2, NULL, 0, tdbb->getAttachment()->att_dec_status, err); // YYY - by SQL II compare_type must be explicit in the // SQL statement if there is any doubt @@ -819,7 +819,7 @@ int INTL_convert_string(dsc* to, const dsc* from, ErrorFunction err) UCHAR* from_ptr; USHORT from_type; - const USHORT from_len = CVT_get_string_ptr(from, &from_type, &from_ptr, NULL, 0, err); + const USHORT from_len = CVT_get_string_ptr(from, &from_type, &from_ptr, NULL, 0, tdbb->getAttachment()->att_dec_status, err); const ULONG to_size = TEXT_LEN(to); ULONG from_fill, to_fill; diff --git a/src/jrd/jrd.cpp b/src/jrd/jrd.cpp index c169f53c17..7e0bba6f64 100644 --- a/src/jrd/jrd.cpp +++ b/src/jrd/jrd.cpp @@ -279,13 +279,15 @@ void JAttachment::addRef() int JAttachment::release() { +#ifdef DEV_BUILD + int r = --refCounter; #ifdef DEBUG_ATT_COUNTERS - int x = --refCounter; ReferenceCounterDebugger* my = ReferenceCounterDebugger::get(DEB_RLS_JATT); const char* point = my ? my->rcd_point : " "; - fprintf(stderr, "Release from <%s> att %p cnt=%d\n", point, this, x); - if (x != 0) - return 1; + fprintf(stderr, "Release from <%s> att %p cnt=%d\n", point, this, r); +#endif + if (r != 0) + return r; #else if (--refCounter != 0) return 1; @@ -1066,8 +1068,8 @@ static void start_transaction(thread_db* tdbb, bool transliterate, jrd_tra** tr static void release_attachment(thread_db*, Jrd::Attachment*); static void rollback(thread_db*, jrd_tra*, const bool); static void purge_attachment(thread_db* tdbb, StableAttachmentPart* sAtt, unsigned flags = 0); -static void getUserInfo(UserId&, const DatabaseOptions&, const char*, const char*, - const RefPtr*, bool, IAttachment*, ICryptKeyCallback*); +static void getUserInfo(UserId&, const DatabaseOptions&, const char*, + const RefPtr*, bool, Mapping& mapping); static THREAD_ENTRY_DECLARE shutdown_thread(THREAD_ENTRY_PARAM); @@ -1080,7 +1082,9 @@ TraceFailedConnection::TraceFailedConnection(const char* filename, const Databas m_filename(filename), m_options(options) { - getUserInfo(m_id, *m_options, m_filename, NULL, NULL, false, NULL, NULL); + Mapping mapping(Mapping::MAP_ERROR_HANDLER, NULL); + mapping.setAuthBlock(m_options->dpb_auth_block); + getUserInfo(m_id, *m_options, m_filename, NULL, false, mapping); } @@ -1359,12 +1363,16 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch PathName org_filename, expanded_name; bool is_alias = false; MutexEnsureUnlock guardDbInit(dbInitMutex, FB_FUNCTION); + Mapping mapping(Mapping::MAP_THROW_NOT_FOUND, cryptCallback); try { // Process database parameter block options.get(dpb, dpb_length, invalid_client_SQL_dialect); + // And provide info about auth block to mapping + mapping.setAuthBlock(options.dpb_auth_block); + if (options.dpb_org_filename.hasData()) org_filename = options.dpb_org_filename; else @@ -1396,6 +1404,9 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch if (ISC_check_if_remote(expanded_name, true)) ERR_post(Arg::Gds(isc_unavailable)); + // We are ready to setup security database - before entering guardDbInit!!! + mapping.setSecurityDbAlias(config->getSecurityDatabase(), expanded_name.c_str()); + #ifdef WIN_NT guardDbInit.enter(); // Required to correctly expand name of just created database @@ -1660,8 +1671,8 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch jAtt->getStable()->manualUnlock(attachment->att_flags); try { - getUserInfo(userId, options, filename, expanded_name.c_str(), - &config, false, jAtt, cryptCallback); + mapping.setDb(filename, expanded_name.c_str(), jAtt); + getUserInfo(userId, options, filename, &config, false, mapping); } catch(const Exception&) { @@ -1953,6 +1964,7 @@ JAttachment* JProvider::internalAttach(CheckStatusWrapper* user_status, const ch filename, options, false, user_status); } + mapping.clearMainHandle(); unwindAttach(tdbb, ex, user_status, attachment, dbb, existingId); } } @@ -2447,12 +2459,14 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch PathName org_filename, expanded_name; bool is_alias = false; Firebird::RefPtr config; + Mapping mapping(Mapping::MAP_THROW_NOT_FOUND, cryptCallback); try { // Process database parameter block bool invalid_client_SQL_dialect = false; options.get(dpb, dpb_length, invalid_client_SQL_dialect); + mapping.setAuthBlock(options.dpb_auth_block); if (!invalid_client_SQL_dialect && options.dpb_sql_dialect == 99) { options.dpb_sql_dialect = 0; } @@ -2490,7 +2504,9 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch ERR_post(Arg::Gds(isc_unavailable)); // Check for correct credentials supplied - getUserInfo(userId, options, filename, NULL, &config, true, nullptr, cryptCallback); + mapping.setSecurityDbAlias(config->getSecurityDatabase(), expanded_name.c_str()); + mapping.setDb(filename, expanded_name.c_str(), nullptr); + getUserInfo(userId, options, filename, &config, true, mapping); #ifdef WIN_NT guardDbInit.enter(); // Required to correctly expand name of just created database @@ -2817,6 +2833,7 @@ JAttachment* JProvider::createDatabase(CheckStatusWrapper* user_status, const ch trace_failed_attach(attachment ? attachment->att_trace_manager : NULL, filename, options, true, user_status); + mapping.clearMainHandle(); unwindAttach(tdbb, ex, user_status, attachment, dbb, false); } } @@ -4188,7 +4205,7 @@ void JProvider::shutdown(CheckStatusWrapper* status, unsigned int timeout, const // Do not put it into separate shutdown thread - during shutdown of TraceManager // PluginManager wants to lock a mutex, which is sometimes already locked in current thread TraceManager::shutdown(); - shutdownMappingIpc(); + Mapping::shutdownIpc(); } catch (const Exception& ex) { @@ -4235,9 +4252,17 @@ JTransaction* JAttachment::startTransaction(CheckStatusWrapper* user_status, successful_completion(user_status); - JTransaction* jt = FB_NEW JTransaction(tra, getStable()); - tra->setInterface(jt); - jt->addRef(); + JTransaction* jt = tra->getInterface(false); + + if (jt) + tra->tra_flags &= ~TRA_own_interface; + else + { + jt = FB_NEW JTransaction(tra, getStable()); + tra->setInterface(jt); + jt->addRef(); + } + return jt; } @@ -7337,9 +7362,8 @@ static VdnResult verifyDatabaseName(const PathName& name, FbStatusVector* status @param cryptCb **/ -static void getUserInfo(UserId& user, const DatabaseOptions& options, - const char* aliasName, const char* dbName, const RefPtr* config, bool creating, - IAttachment* iAtt, ICryptKeyCallback* cryptCb) +static void getUserInfo(UserId& user, const DatabaseOptions& options, const char* aliasName, + const RefPtr* config, bool creating, Mapping& mapping) { bool wheel = false; int id = -1, group = -1; // CVC: This var contained trash @@ -7365,9 +7389,10 @@ static void getUserInfo(UserId& user, const DatabaseOptions& options, } else if (options.dpb_auth_block.hasData()) { - if (mapUser(true, name, trusted_role, &auth_method, &user.usr_auth_block, NULL, - options.dpb_auth_block, aliasName, dbName, - (config ? (*config)->getSecurityDatabase() : NULL), "", cryptCb, iAtt) & MAPUSER_MAP_DOWN) + mapping.needAuthMethod(auth_method); + mapping.needAuthBlock(user.usr_auth_block); + + if (mapping.mapUser(name, trusted_role) & Mapping::MAP_DOWN) { user.setFlag(USR_mapdown); } diff --git a/src/jrd/jrd.h b/src/jrd/jrd.h index d07c209fe1..16aca4f54a 100644 --- a/src/jrd/jrd.h +++ b/src/jrd/jrd.h @@ -67,9 +67,10 @@ #endif #define DEBUG -#define BUGCHECK(number) ERR_bugcheck (number, __FILE__, __LINE__) -#define CORRUPT(number) ERR_corrupt (number) -#define IBERROR(number) ERR_error (number) +#define BUGCHECK(number) ERR_bugcheck(number, __FILE__, __LINE__) +#define SOFT_BUGCHECK(number) ERR_soft_bugcheck(number, __FILE__, __LINE__) +#define CORRUPT(number) ERR_corrupt(number) +#define IBERROR(number) ERR_error(number) #define BLKCHK(blk, type) if (!blk->checkHandle()) BUGCHECK(147) diff --git a/src/jrd/mov.cpp b/src/jrd/mov.cpp index 816daa1409..1b9d85803c 100644 --- a/src/jrd/mov.cpp +++ b/src/jrd/mov.cpp @@ -42,7 +42,7 @@ using namespace Firebird; -int MOV_compare(const dsc* arg1, const dsc* arg2) +int MOV_compare(Jrd::thread_db* tdbb, const dsc* arg1, const dsc* arg2) { /************************************** * @@ -55,7 +55,7 @@ int MOV_compare(const dsc* arg1, const dsc* arg2) * **************************************/ - return CVT2_compare(arg1, arg2); + return CVT2_compare(arg1, arg2, tdbb->getAttachment()->att_dec_status); } @@ -102,7 +102,7 @@ bool MOV_get_boolean(const dsc* desc) } -double MOV_get_double(const dsc* desc) +double MOV_get_double(Jrd::thread_db* tdbb, const dsc* desc) { /************************************** * @@ -115,11 +115,11 @@ double MOV_get_double(const dsc* desc) * **************************************/ - return CVT_get_double(desc, ERR_post); + return CVT_get_double(desc, tdbb->getAttachment()->att_dec_status, ERR_post); } -SLONG MOV_get_long(const dsc* desc, SSHORT scale) +SLONG MOV_get_long(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) { /************************************** * @@ -133,11 +133,11 @@ SLONG MOV_get_long(const dsc* desc, SSHORT scale) * **************************************/ - return CVT_get_long(desc, scale, ERR_post); + return CVT_get_long(desc, scale, tdbb->getAttachment()->att_dec_status, ERR_post); } -SINT64 MOV_get_int64(const dsc* desc, SSHORT scale) +SINT64 MOV_get_int64(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) { /************************************** * @@ -151,11 +151,11 @@ SINT64 MOV_get_int64(const dsc* desc, SSHORT scale) * **************************************/ - return CVT_get_int64(desc, scale, ERR_post); + return CVT_get_int64(desc, scale, tdbb->getAttachment()->att_dec_status, ERR_post); } -void MOV_get_metaname(const dsc* desc, MetaName& name) +void MOV_get_metaname(Jrd::thread_db* tdbb, const dsc* desc, MetaName& name) { /************************************** * @@ -172,7 +172,7 @@ void MOV_get_metaname(const dsc* desc, MetaName& name) USHORT ttype; UCHAR* ptr = NULL; - const USHORT length = CVT_get_string_ptr(desc, &ttype, &ptr, NULL, 0); + const USHORT length = CVT_get_string_ptr(desc, &ttype, &ptr, NULL, 0, tdbb->getAttachment()->att_dec_status); fb_assert(length && ptr); fb_assert(length <= MAX_SQL_IDENTIFIER_LEN); @@ -182,7 +182,7 @@ void MOV_get_metaname(const dsc* desc, MetaName& name) } -SQUAD MOV_get_quad(const dsc* desc, SSHORT scale) +SQUAD MOV_get_quad(Jrd::thread_db* tdbb, const dsc* desc, SSHORT scale) { /************************************** * @@ -196,11 +196,12 @@ SQUAD MOV_get_quad(const dsc* desc, SSHORT scale) * **************************************/ - return CVT_get_quad(desc, scale, ERR_post); + return CVT_get_quad(desc, scale, tdbb->getAttachment()->att_dec_status, ERR_post); } -int MOV_get_string_ptr(const dsc* desc, +int MOV_get_string_ptr(Jrd::thread_db* tdbb, + const dsc* desc, USHORT* ttype, UCHAR** address, vary* temp, USHORT length) { @@ -221,11 +222,11 @@ int MOV_get_string_ptr(const dsc* desc, * **************************************/ - return CVT_get_string_ptr(desc, ttype, address, temp, length); + return CVT_get_string_ptr(desc, ttype, address, temp, length, tdbb->getAttachment()->att_dec_status); } -int MOV_get_string(const dsc* desc, UCHAR** address, vary* temp, USHORT length) +int MOV_get_string(Jrd::thread_db* tdbb, const dsc* desc, UCHAR** address, vary* temp, USHORT length) { /************************************** * @@ -238,7 +239,7 @@ int MOV_get_string(const dsc* desc, UCHAR** address, vary* temp, USHORT length) **************************************/ USHORT ttype; - return MOV_get_string_ptr(desc, &ttype, address, temp, length); + return MOV_get_string_ptr(tdbb, desc, &ttype, address, temp, length); } @@ -293,7 +294,8 @@ GDS_TIMESTAMP MOV_get_timestamp(const dsc* desc) } -int MOV_make_string(const dsc* desc, +int MOV_make_string(Jrd::thread_db* tdbb, + const dsc* desc, USHORT ttype, const char** address, vary* temp, @@ -318,7 +320,7 @@ int MOV_make_string(const dsc* desc, * **************************************/ - return CVT_make_string(desc, ttype, address, temp, length, ERR_post); + return CVT_make_string(desc, ttype, address, temp, length, tdbb->getAttachment()->att_dec_status, ERR_post); } @@ -380,7 +382,7 @@ int MOV_make_string2(Jrd::thread_db* tdbb, return size; } - return CVT2_make_string2(desc, ttype, address, buffer); + return CVT2_make_string2(desc, ttype, address, buffer, tdbb->getAttachment()->att_dec_status); } @@ -410,9 +412,34 @@ void MOV_move(Jrd::thread_db* tdbb, /*const*/ dsc* from, dsc* to) if (DTYPE_IS_BLOB_OR_QUAD(from->dsc_dtype) || DTYPE_IS_BLOB_OR_QUAD(to->dsc_dtype)) Jrd::blb::move(tdbb, from, to, NULL); else - CVT_move(from, to); + CVT_move(from, to, tdbb->getAttachment()->att_dec_status); } + +Decimal64 MOV_get_dec64(Jrd::thread_db* tdbb, const dsc* desc) +{ +/************************************** + * + * M O V _ g e t _ d e c 6 4 + * + **************************************/ + + return CVT_get_dec64(desc, tdbb->getAttachment()->att_dec_status, ERR_post); +} + + +Decimal128 MOV_get_dec128(Jrd::thread_db* tdbb, const dsc* desc) +{ +/************************************** + * + * M O V _ g e t _ d e c 1 2 8 + * + **************************************/ + + return CVT_get_dec128(desc, tdbb->getAttachment()->att_dec_status, ERR_post); +} + + namespace Jrd { diff --git a/src/jrd/mov_proto.h b/src/jrd/mov_proto.h index 7d97076ec7..b68002a5ba 100644 --- a/src/jrd/mov_proto.h +++ b/src/jrd/mov_proto.h @@ -31,26 +31,27 @@ struct dsc; struct vary; -int MOV_compare(const dsc*, const dsc*); +int MOV_compare(Jrd::thread_db*, const dsc*, const dsc*); double MOV_date_to_double(const dsc*); void MOV_double_to_date(double, SLONG[2]); bool MOV_get_boolean(const dsc*); -double MOV_get_double(const dsc*); -SLONG MOV_get_long(const dsc*, SSHORT); -void MOV_get_metaname(const dsc*, Firebird::MetaName&); -SQUAD MOV_get_quad(const dsc*, SSHORT); -SINT64 MOV_get_int64(const dsc*, SSHORT); -int MOV_get_string_ptr(const dsc*, USHORT*, UCHAR**, vary*, - USHORT); -int MOV_get_string(const dsc*, UCHAR**, vary*, USHORT); +double MOV_get_double(Jrd::thread_db*, const dsc*); +SLONG MOV_get_long(Jrd::thread_db*, const dsc*, SSHORT); +void MOV_get_metaname(Jrd::thread_db*, const dsc*, Firebird::MetaName&); +SQUAD MOV_get_quad(Jrd::thread_db*, const dsc*, SSHORT); +SINT64 MOV_get_int64(Jrd::thread_db*, const dsc*, SSHORT); +int MOV_get_string_ptr(Jrd::thread_db*, const dsc*, USHORT*, UCHAR**, vary*, USHORT); +int MOV_get_string(Jrd::thread_db*, const dsc*, UCHAR**, vary*, USHORT); GDS_DATE MOV_get_sql_date(const dsc*); GDS_TIME MOV_get_sql_time(const dsc*); GDS_TIMESTAMP MOV_get_timestamp(const dsc*); -int MOV_make_string(const dsc*, USHORT, const char**, vary*, USHORT); +int MOV_make_string(Jrd::thread_db*, const dsc*, USHORT, const char**, vary*, USHORT); int MOV_make_string2(Jrd::thread_db*, const dsc*, USHORT, UCHAR**, Jrd::MoveBuffer&, bool = true); Firebird::string MOV_make_string2(Jrd::thread_db* tdbb, const dsc* desc, USHORT ttype, bool limit = true); void MOV_move(Jrd::thread_db*, /*const*/ dsc*, dsc*); +Firebird::Decimal64 MOV_get_dec64(Jrd::thread_db*, const dsc*); +Firebird::Decimal128 MOV_get_dec128(Jrd::thread_db*, const dsc*); namespace Jrd { diff --git a/src/jrd/opt.cpp b/src/jrd/opt.cpp index aa70131986..dd4442255f 100644 --- a/src/jrd/opt.cpp +++ b/src/jrd/opt.cpp @@ -394,7 +394,9 @@ static const UCHAR sort_dtypes[] = 0, // dtype_array SKD_int64, // dtype_int64 SKD_text, // dtype_dbkey - use text sort for backward compatibility - SKD_bytes // dtype_boolean + SKD_bytes, // dtype_boolean + SKD_dec64, // dtype_dec64 + SKD_dec128 // dtype_dec128 }; @@ -1115,8 +1117,8 @@ static void check_sorts(RseNode* rse) for (; project_ptr != project_end; ++project_ptr) { - const FieldNode* sortField = (*sort_ptr)->as(); - const FieldNode* projectField = (*project_ptr)->as(); + const FieldNode* sortField = nodeAs(*sort_ptr); + const FieldNode* projectField = nodeAs(*project_ptr); if (sortField && projectField && sortField->fieldStream == projectField->fieldStream && @@ -1155,7 +1157,7 @@ static void check_sorts(RseNode* rse) { const FieldNode* sortField; - if ((sortField = (*sort_ptr)->as())) + if ((sortField = nodeAs(*sort_ptr))) { // Get stream for this field at this position. const StreamType current_stream = sortField->fieldStream; @@ -1327,8 +1329,8 @@ static SLONG decompose(thread_db* tdbb, BoolExprNode* boolNode, BoolExprNodeStac **************************************/ DEV_BLKCHK(csb, type_csb); - BinaryBoolNode* binaryNode = boolNode->as(); - ComparativeBoolNode* cmpNode = boolNode->as(); + BinaryBoolNode* binaryNode = nodeAs(boolNode); + ComparativeBoolNode* cmpNode = nodeAs(boolNode); if (binaryNode) { @@ -1467,17 +1469,17 @@ static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratc if (boolean->nodFlags & ExprNode::FLAG_DEOPTIMIZE) continue; - ComparativeBoolNode* const cmpNode = boolean->as(); + ComparativeBoolNode* const cmpNode = nodeAs(boolean); if (!cmpNode || cmpNode->blrOp != blr_eql) continue; ValueExprNode* const node1 = cmpNode->arg1; - if (!node1->is()) + if (!nodeIs(node1)) continue; ValueExprNode* const node2 = cmpNode->arg2; - if (!node2->is()) + if (!nodeIs(node2)) continue; for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) @@ -1560,7 +1562,7 @@ static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratc for (BoolExprNodeStack::iterator iter(org_stack); iter.hasData(); ++iter) { BoolExprNode* const boolean = iter.object(); - ComparativeBoolNode* const cmpNode = boolean->as(); + ComparativeBoolNode* const cmpNode = nodeAs(boolean); ValueExprNode* node1; ValueExprNode* node2; @@ -1578,7 +1580,7 @@ static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratc bool reverse = false; - if (!node1->is()) + if (!nodeIs(node1)) { ValueExprNode* swap_node = node1; node1 = node2; @@ -1586,10 +1588,10 @@ static USHORT distribute_equalities(BoolExprNodeStack& org_stack, CompilerScratc reverse = true; } - if (!node1->is()) + if (!nodeIs(node1)) continue; - if (!node2->is() && !node2->is() && !node2->is()) + if (!nodeIs(node2) && !nodeIs(node2) && !nodeIs(node2)) continue; for (eq_class = classes.begin(); eq_class != classes.end(); ++eq_class) @@ -1881,7 +1883,7 @@ void OPT_gen_aggregate_distincts(thread_db* tdbb, CompilerScratch* csb, MapNode* for (const NestConst* const end = map->sourceList.end(); ptr != end; ++ptr) { ValueExprNode* from = *ptr; - AggNode* aggNode = from->as(); + AggNode* aggNode = nodeAs(from); if (aggNode && aggNode->distinct) { @@ -1900,39 +1902,36 @@ void OPT_gen_aggregate_distincts(thread_db* tdbb, CompilerScratch* csb, MapNode* desc->getTextType() != ttype_binary && desc->getTextType() != ttype_ascii; sort_key_def* sort_key = asb->keyItems.getBuffer(asb->intl ? 2 : 1); - sort_key->skd_offset = 0; + sort_key->setSkdOffset(); if (asb->intl) { const USHORT key_length = ROUNDUP(INTL_key_length(tdbb, INTL_TEXT_TO_INDEX(desc->getTextType()), desc->getStringLength()), sizeof(SINT64)); - sort_key->skd_dtype = SKD_bytes; + sort_key->setSkdLength(SKD_bytes, key_length); sort_key->skd_flags = SKD_ascending; - sort_key->skd_length = key_length; - sort_key->skd_offset = 0; sort_key->skd_vary_offset = 0; ++sort_key; - asb->length = sort_key->skd_offset = key_length; + sort_key->setSkdOffset(&sort_key[-1]); + asb->length = sort_key->getSkdOffset(); } fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); - sort_key->skd_dtype = sort_dtypes[desc->dsc_dtype]; + sort_key->setSkdLength(sort_dtypes[desc->dsc_dtype], desc->dsc_length); if (!sort_key->skd_dtype) ERR_post(Arg::Gds(isc_invalid_sort_datatype) << Arg::Str(DSC_dtype_tostring(desc->dsc_dtype))); - sort_key->skd_length = desc->dsc_length; - if (desc->dsc_dtype == dtype_varying) { // allocate space to store varying length - sort_key->skd_vary_offset = sort_key->skd_offset + ROUNDUP(desc->dsc_length, sizeof(SLONG)); + sort_key->skd_vary_offset = sort_key->getSkdOffset() + ROUNDUP(desc->dsc_length, sizeof(SLONG)); asb->length = sort_key->skd_vary_offset + sizeof(USHORT); } else - asb->length += sort_key->skd_length; + asb->length += sort_key->getSkdLength(); asb->length = ROUNDUP(asb->length, sizeof(SLONG)); // dimitr: allocate an extra longword for the purely artificial counter, @@ -2446,7 +2445,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi node_ptr != end_node; ++node_ptr) { - FieldNode* fieldNode = (*node_ptr)->as(); + FieldNode* fieldNode = nodeAs(*node_ptr); if (fieldNode && fieldNode->fieldStream == *ptr && fieldNode->fieldId == id) { @@ -2454,7 +2453,8 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi fieldNode->getDesc(tdbb, csb, desc); // International type text has a computed key - if (IS_INTL_DATA(desc)) + // Different decimal float values sometimes have same keys + if (IS_INTL_DATA(desc) || desc->isDecFloat()) break; --items; @@ -2478,7 +2478,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi if (sort->unique) map->flags |= SortedStream::FLAG_UNIQUE; - ULONG map_length = 0; + sort_key_def* prev_key = nullptr; // Loop thru sort keys building sort keys. Actually, to handle null values // correctly, two sort keys are made for each field, one for the null flag @@ -2519,14 +2519,9 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi } // Make key for null flag + sort_key->setSkdLength(SKD_text, 1); + sort_key->setSkdOffset(prev_key); -#ifndef WORDS_BIGENDIAN - map_length = ROUNDUP(map_length, sizeof(SLONG)); -#endif - const ULONG flag_offset = map_length++; - sort_key->skd_offset = flag_offset; - sort_key->skd_dtype = SKD_text; - sort_key->skd_length = 1; // Handle nulls placement sort_key->skd_flags = SKD_ascending; @@ -2534,22 +2529,15 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi if ((*nullOrder == rse_nulls_default && !*descending) || *nullOrder == rse_nulls_first) sort_key->skd_flags |= SKD_descending; - ++sort_key; + prev_key = sort_key++; // Make key for sort key proper -#ifndef WORDS_BIGENDIAN - map_length = ROUNDUP(map_length, sizeof(SLONG)); -#else - if (desc->dsc_dtype >= dtype_aligned) - map_length = FB_ALIGN(map_length, type_alignments[desc->dsc_dtype]); -#endif - - sort_key->skd_offset = map_length; + fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); + sort_key->setSkdLength(sort_dtypes[desc->dsc_dtype], desc->dsc_length); + sort_key->setSkdOffset(&sort_key[-1], desc); sort_key->skd_flags = SKD_ascending; if (*descending) sort_key->skd_flags |= SKD_descending; - fb_assert(desc->dsc_dtype < FB_NELEM(sort_dtypes)); - sort_key->skd_dtype = sort_dtypes[desc->dsc_dtype]; if (!sort_key->skd_dtype) ERR_post(Arg::Gds(isc_invalid_sort_datatype) << Arg::Str(DSC_dtype_tostring(desc->dsc_dtype))); @@ -2560,25 +2548,25 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi sort_key->skd_flags |= SKD_binary; } - sort_key->skd_length = desc->dsc_length; - ++sort_key; map_item->clear(); map_item->node = node; - map_item->flagOffset = flag_offset; + map_item->flagOffset = prev_key->getSkdOffset(); map_item->desc = *desc; - map_item->desc.dsc_address = (UCHAR*)(IPTR) map_length; - map_length += desc->dsc_length; + map_item->desc.dsc_address = (UCHAR*)(IPTR) sort_key->getSkdOffset(); + + prev_key = sort_key++; FieldNode* fieldNode; - if ((fieldNode = node->as())) + if ((fieldNode = nodeAs(node))) { map_item->stream = fieldNode->fieldStream; map_item->fieldId = fieldNode->fieldId; } } - map_length = ROUNDUP(map_length, sizeof(SLONG)); + fb_assert(prev_key); + ULONG map_length = prev_key ? ROUNDUP(prev_key->getSkdOffset() + prev_key->getSkdLength(), sizeof(SLONG)) : 0; map->keyLength = map_length; ULONG flag_offset = map_length; map_length += stream_list.getCount(); @@ -2596,6 +2584,7 @@ SortedStream* OPT_gen_sort(thread_db* tdbb, CompilerScratch* csb, const StreamLi IBERROR(157); // msg 157 cannot sort on a field that does not exist if (desc->dsc_dtype >= dtype_aligned) map_length = FB_ALIGN(map_length, type_alignments[desc->dsc_dtype]); + map_item->clear(); map_item->fieldId = (SSHORT) id; map_item->stream = stream; @@ -2759,7 +2748,7 @@ static bool gen_equi_join(thread_db* tdbb, OptimizerBlk* opt, RiverList& org_riv continue; BoolExprNode* const node = tail->opt_conjunct_node; - ComparativeBoolNode* cmpNode = node->as(); + ComparativeBoolNode* cmpNode = nodeAs(node); if (!cmpNode || (cmpNode->blrOp != blr_eql && cmpNode->blrOp != blr_equiv)) continue; @@ -3088,7 +3077,7 @@ static BoolExprNode* make_inference_node(CompilerScratch* csb, BoolExprNode* boo thread_db* tdbb = JRD_get_thread_data(); DEV_BLKCHK(csb, type_csb); - ComparativeBoolNode* cmpNode = boolean->as(); + ComparativeBoolNode* cmpNode = nodeAs(boolean); fb_assert(cmpNode); // see our caller // Clone the input predicate @@ -3136,8 +3125,8 @@ static bool map_equal(const ValueExprNode* field1, const ValueExprNode* field2, * Order of the input fields is important. * **************************************/ - const FieldNode* fieldNode1 = field1->as(); - const FieldNode* fieldNode2 = field2->as(); + const FieldNode* fieldNode1 = nodeAs(field1); + const FieldNode* fieldNode2 = nodeAs(field2); if (!fieldNode1 || !fieldNode2) return false; @@ -3150,8 +3139,8 @@ static bool map_equal(const ValueExprNode* field1, const ValueExprNode* field2, sourcePtr != sourceEnd; ++sourcePtr, ++targetPtr) { - const FieldNode* mapFrom = (*sourcePtr)->as(); - const FieldNode* mapTo = (*targetPtr)->as(); + const FieldNode* mapFrom = nodeAs(*sourcePtr); + const FieldNode* mapTo = nodeAs(*targetPtr); if (!mapFrom || !mapTo) continue; @@ -3241,8 +3230,8 @@ static bool node_equality(const ValueExprNode* node1, const ValueExprNode* node2 if (node1 == node2) return true; - const FieldNode* fieldNode1 = node1->as(); - const FieldNode* fieldNode2 = node2->as(); + const FieldNode* fieldNode1 = nodeAs(node1); + const FieldNode* fieldNode2 = nodeAs(node2); if (fieldNode1 && fieldNode2) { @@ -3267,8 +3256,8 @@ static bool node_equality(const BoolExprNode* node1, const BoolExprNode* node2) if (node1 == node2) return true; - const ComparativeBoolNode* cmpNode = node1->as(); - const ComparativeBoolNode* cmpNode2 = node2->as(); + const ComparativeBoolNode* cmpNode = nodeAs(node1); + const ComparativeBoolNode* cmpNode2 = nodeAs(node2); if (cmpNode && cmpNode2 && cmpNode->blrOp == cmpNode2->blrOp && (cmpNode->blrOp == blr_eql || cmpNode->blrOp == blr_equiv)) @@ -3318,17 +3307,17 @@ static ValueExprNode* optimize_like(thread_db* tdbb, CompilerScratch* csb, Compa // if the pattern string or the escape string can't be // evaluated at compile time, forget it - if (!pattern_node->is() || (escape_node && !escape_node->is())) + if (!nodeIs(pattern_node) || (escape_node && !nodeIs(escape_node))) return NULL; dsc match_desc; match_node->getDesc(tdbb, csb, &match_desc); - dsc* pattern_desc = &pattern_node->as()->litDesc; + dsc* pattern_desc = &nodeAs(pattern_node)->litDesc; dsc* escape_desc = NULL; if (escape_node) - escape_desc = &escape_node->as()->litDesc; + escape_desc = &nodeAs(escape_node)->litDesc; // if either is not a character expression, forget it if ((match_desc.dsc_dtype > dtype_any_text) || @@ -3555,8 +3544,8 @@ static void set_position(const SortNode* from_clause, SortNode* to_clause, const for (const NestConst* const to_end = to_ptr + count; to_ptr != to_end; ++to_ptr) { - const FieldNode* fromField = (*from_ptr)->as(); - const FieldNode* toField = (*to_ptr)->as(); + const FieldNode* fromField = nodeAs(*from_ptr); + const FieldNode* toField = nodeAs(*to_ptr); if ((map && map_equal(*to_ptr, *from_ptr, map)) || (!map && diff --git a/src/jrd/pag.cpp b/src/jrd/pag.cpp index 2f80da4c6e..a00321ac56 100644 --- a/src/jrd/pag.cpp +++ b/src/jrd/pag.cpp @@ -1712,6 +1712,14 @@ void PAG_set_db_readonly(thread_db* tdbb, bool flag) // for WRITE operations header->hdr_flags &= ~hdr_read_only; dbb->dbb_flags &= ~DBB_read_only; + + // This is necessary as dbb's Next could be less than OAT. + // And this is safe as we currently in exclusive attachment and + // all executed transactions was read-only. + dbb->dbb_next_transaction = Ods::getNT(header); + dbb->dbb_oldest_transaction = Ods::getOIT(header); + dbb->dbb_oldest_active = Ods::getOAT(header); + dbb->dbb_oldest_snapshot = Ods::getOST(header); } CCH_MARK_MUST_WRITE(tdbb, &window); diff --git a/src/jrd/par.cpp b/src/jrd/par.cpp index 1e93c6a1ca..06a2e9eb13 100644 --- a/src/jrd/par.cpp +++ b/src/jrd/par.cpp @@ -385,6 +385,16 @@ USHORT PAR_datatype(BlrReader& blrReader, dsc* desc) desc->dsc_length = sizeof(double); break; + case blr_dec64: + desc->dsc_dtype = dtype_dec64; + desc->dsc_length = sizeof(Decimal64); + break; + + case blr_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + break; + case blr_blob2: desc->dsc_dtype = dtype_blob; desc->dsc_length = sizeof(ISC_QUAD); diff --git a/src/jrd/recsrc/AggregatedStream.cpp b/src/jrd/recsrc/AggregatedStream.cpp index 709cd31014..c3d2985f7c 100644 --- a/src/jrd/recsrc/AggregatedStream.cpp +++ b/src/jrd/recsrc/AggregatedStream.cpp @@ -205,11 +205,11 @@ void BaseAggWinStream::aggInit(thread_db* tdbb, jrd_req* req source != sourceEnd; ++source, ++target) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (aggNode) aggNode->aggInit(tdbb, request); - else if ((*source)->is()) + else if (nodeIs(*source)) EXE_assignment(tdbb, *source, *target); } } @@ -227,7 +227,7 @@ bool BaseAggWinStream::aggPass(thread_db* tdbb, jrd_req* req source != sourceEnd; ++source, ++target) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (aggNode) { @@ -256,11 +256,11 @@ void BaseAggWinStream::aggExecute(thread_db* tdbb, jrd_req* source != sourceEnd; ++source, ++target) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (aggNode) { - const FieldNode* field = (*target)->as(); + const FieldNode* field = nodeAs(*target); const USHORT id = field->fieldId; Record* record = request->req_rpb[field->fieldStream].rpb_record; @@ -287,7 +287,7 @@ void BaseAggWinStream::aggFinish(thread_db* tdbb, jrd_req* r source != sourceEnd; ++source) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (aggNode) aggNode->aggFinish(tdbb, request); @@ -334,7 +334,7 @@ int BaseAggWinStream::lookForChange(thread_db* tdbb, jrd_req } else if (!vtemp->vlu_desc.dsc_address) return 1 * nullDirection; - else if ((n = MOV_compare(desc, &vtemp->vlu_desc)) != 0) + else if ((n = MOV_compare(tdbb, desc, &vtemp->vlu_desc)) != 0) return n * direction; } diff --git a/src/jrd/recsrc/FilteredStream.cpp b/src/jrd/recsrc/FilteredStream.cpp index e9586abdac..f4dd0c3f82 100644 --- a/src/jrd/recsrc/FilteredStream.cpp +++ b/src/jrd/recsrc/FilteredStream.cpp @@ -159,7 +159,7 @@ bool FilteredStream::evaluateBoolean(thread_db* tdbb) const { // see if there's a select node to work with - const BinaryBoolNode* booleanNode = column_node->as(); + const BinaryBoolNode* booleanNode = nodeAs(column_node); if (booleanNode && booleanNode->blrOp == blr_and) { diff --git a/src/jrd/recsrc/FirstRowsStream.cpp b/src/jrd/recsrc/FirstRowsStream.cpp index bbb6bf1ec1..c7566b8ef7 100644 --- a/src/jrd/recsrc/FirstRowsStream.cpp +++ b/src/jrd/recsrc/FirstRowsStream.cpp @@ -52,7 +52,7 @@ void FirstRowsStream::open(thread_db* tdbb) const impure->irsb_flags = 0; const dsc* desc = EVL_expr(tdbb, request, m_value); - const SINT64 value = (desc && !(request->req_flags & req_null)) ? MOV_get_int64(desc, 0) : 0; + const SINT64 value = (desc && !(request->req_flags & req_null)) ? MOV_get_int64(tdbb, desc, 0) : 0; if (value < 0) status_exception::raise(Arg::Gds(isc_bad_limit_param)); diff --git a/src/jrd/recsrc/MergeJoin.cpp b/src/jrd/recsrc/MergeJoin.cpp index e5aff52dbc..50c47e31bf 100644 --- a/src/jrd/recsrc/MergeJoin.cpp +++ b/src/jrd/recsrc/MergeJoin.cpp @@ -409,7 +409,7 @@ int MergeJoin::compare(thread_db* tdbb, const NestValueArray* node1, if (!null1 && !null2) { - const int result = MOV_compare(desc1, desc2); + const int result = MOV_compare(tdbb, desc1, desc2); if (result != 0) return result; diff --git a/src/jrd/recsrc/SkipRowsStream.cpp b/src/jrd/recsrc/SkipRowsStream.cpp index 7528389dc7..975bbe85db 100644 --- a/src/jrd/recsrc/SkipRowsStream.cpp +++ b/src/jrd/recsrc/SkipRowsStream.cpp @@ -52,7 +52,7 @@ void SkipRowsStream::open(thread_db* tdbb) const impure->irsb_flags = irsb_open; const dsc* desc = EVL_expr(tdbb, request, m_value); - const SINT64 value = (desc && !(request->req_flags & req_null)) ? MOV_get_int64(desc, 0) : 0; + const SINT64 value = (desc && !(request->req_flags & req_null)) ? MOV_get_int64(tdbb, desc, 0) : 0; if (value < 0) { diff --git a/src/jrd/recsrc/SortedStream.cpp b/src/jrd/recsrc/SortedStream.cpp index 57caf47de2..aa0e5ad638 100644 --- a/src/jrd/recsrc/SortedStream.cpp +++ b/src/jrd/recsrc/SortedStream.cpp @@ -276,6 +276,7 @@ bool SortedStream::compareKeys(const UCHAR* p, const UCHAR* q) const fb_assert(m_map->keyItems.getCount() % 2 == 0); const USHORT count = m_map->keyItems.getCount() / 2; + thread_db* tdbb = JRD_get_thread_data(); for (USHORT i = 0; i < count; i++) { @@ -295,7 +296,7 @@ bool SortedStream::compareKeys(const UCHAR* p, const UCHAR* q) const dsc desc2 = item->desc; desc2.dsc_address = const_cast(q) + (IPTR) desc2.dsc_address; - if (MOV_compare(&desc1, &desc2)) + if (MOV_compare(tdbb, &desc1, &desc2)) return false; } } @@ -328,7 +329,7 @@ void SortedStream::mapData(thread_db* tdbb, jrd_req* request, UCHAR* data) const from = item->desc; from.dsc_address = data + (IPTR) from.dsc_address; - if (item->node && !item->node->is()) + if (item->node && !nodeIs(item->node)) continue; // if moving a TEXT item into the key portion of the sort record, @@ -338,7 +339,7 @@ void SortedStream::mapData(thread_db* tdbb, jrd_req* request, UCHAR* data) const // a sort key, there is a later nod_field in the item // list that contains the data to send back - if (IS_INTL_DATA(&item->desc) && + if ((IS_INTL_DATA(&item->desc) || item->desc.isDecFloat()) && (ULONG)(IPTR) item->desc.dsc_address < m_map->keyLength) { continue; diff --git a/src/jrd/recsrc/WindowedStream.cpp b/src/jrd/recsrc/WindowedStream.cpp index 9ed08f033e..faf18764ce 100644 --- a/src/jrd/recsrc/WindowedStream.cpp +++ b/src/jrd/recsrc/WindowedStream.cpp @@ -205,7 +205,7 @@ WindowedStream::WindowedStream(thread_db* tdbb, CompilerScratch* csb, source != end; ++source) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (aggNode) { @@ -443,7 +443,7 @@ WindowedStream::WindowStream::WindowStream(thread_db* tdbb, CompilerScratch* csb source != sourceEnd; ++source, ++target) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (aggNode) { @@ -494,9 +494,9 @@ WindowedStream::WindowStream::WindowStream(thread_db* tdbb, CompilerScratch* csb //// TODO: Better check for invariants. if (frame->value && - (frame->value->is() || - frame->value->is() || - frame->value->is())) + (nodeIs(frame->value) || + nodeIs(frame->value) || + nodeIs(frame->value))) { m_invariantOffsets |= i == 0 ? 0x1 : 0x2; } @@ -839,9 +839,9 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const source != sourceEnd; ++source, ++target) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); - const FieldNode* field = (*target)->as(); + const FieldNode* field = nodeAs(*target); const USHORT id = field->fieldId; Record* record = request->req_rpb[field->fieldStream].rpb_record; @@ -869,7 +869,7 @@ bool WindowedStream::WindowStream::getRecord(thread_db* tdbb) const source != sourceEnd; ++source, ++target) { - const AggNode* aggNode = (*source)->as(); + const AggNode* aggNode = nodeAs(*source); if (!aggNode) EXE_assignment(tdbb, *source, *target); @@ -916,7 +916,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* if (m_frameExtent->unit == WindowClause::FrameExtent::Unit::ROWS) { // Purposedly used 32-bit here. So long distance will complicate things for no gain. - impureValue->vlux_count = MOV_get_long(desc, 0); + impureValue->vlux_count = MOV_get_long(tdbb, desc, 0); if (impureValue->vlux_count < 0) error = true; @@ -924,7 +924,7 @@ const void WindowedStream::WindowStream::getFrameValue(thread_db* tdbb, jrd_req* if (frame->bound == WindowClause::Frame::Bound::PRECEDING) impureValue->vlux_count = -impureValue->vlux_count; } - else if (MOV_compare(desc, &zeroDsc) < 0) + else if (MOV_compare(tdbb, desc, &zeroDsc) < 0) error = true; if (!error) diff --git a/src/jrd/sort.cpp b/src/jrd/sort.cpp index 856b390c74..8a5e03cfa2 100644 --- a/src/jrd/sort.cpp +++ b/src/jrd/sort.cpp @@ -213,7 +213,7 @@ Sort::Sort(Database* dbb, const sort_key_def* p = m_description.end() - 1; - m_key_length = ROUNDUP(p->skd_offset + p->skd_length, sizeof(SLONG)) >> SHIFTLONG; + m_key_length = ROUNDUP(p->getSkdOffset() + p->getSkdLength(), sizeof(SLONG)) >> SHIFTLONG; while (unique_keys < keys) { @@ -221,7 +221,7 @@ Sort::Sort(Database* dbb, unique_keys++; } - m_unique_length = ROUNDUP(p->skd_offset + p->skd_length, sizeof(SLONG)) >> SHIFTLONG; + m_unique_length = ROUNDUP(p->getSkdOffset() + p->getSkdLength(), sizeof(SLONG)) >> SHIFTLONG; // Next, try to allocate a "big block". How big? Big enough! @@ -711,8 +711,8 @@ void Sort::diddleKey(UCHAR* record, bool direction) for (sort_key_def* key = m_description.begin(), *end = m_description.end(); key < end; key++) { - UCHAR* p = record + key->skd_offset; - USHORT n = key->skd_length; + UCHAR* p = record + key->getSkdOffset(); + USHORT n = key->getSkdLength(); USHORT complement = key->skd_flags & SKD_descending; // This trick replaces possibly negative zero with positive zero, so that both @@ -806,6 +806,49 @@ void Sort::diddleKey(UCHAR* record, bool direction) *p ^= 1 << 7; break; + case SKD_dec64: + if (direction) + { + ((Decimal64*) p)->makeKey(lwp); + *p ^= 1 << 7; + } + else + { + if (complement && n) + { + UCHAR* pp = p; + do { + *pp++ ^= -1; + } while (--n); + } + + *p ^= 1 << 7; + ((Decimal64*) p)->grabKey(lwp); + } + break; + + case SKD_dec128: + fb_assert(false); // diddleKey for Dec64/128 not tested on bigendians! + if (direction) + { + ((Decimal128*) p)->makeKey(lwp); + *p ^= 1 << 7; + } + else + { + if (complement && n) + { + UCHAR* pp = p; + do { + *pp++ ^= -1; + } while (--n); + } + + *p ^= 1 << 7; + ((Decimal128*) p)->grabKey(lwp); + } + break; + default: fb_assert(false); break; @@ -824,13 +867,13 @@ void Sort::diddleKey(UCHAR* record, bool direction) if (key->skd_dtype == SKD_varying && !direction) { - p = record + key->skd_offset; + p = record + key->getSkdOffset(); ((vary*) p)->vary_length = *((USHORT*) (record + key->skd_vary_offset)); } if (key->skd_dtype == SKD_cstring && !direction) { - p = record + key->skd_offset; + p = record + key->getSkdOffset(); USHORT l = *((USHORT*) (record + key->skd_vary_offset)); *(p + l) = 0; } @@ -858,11 +901,11 @@ void Sort::diddleKey(UCHAR* record, bool direction) for (sort_key_def* key = m_description.begin(), *end = m_description.end(); key < end; key++) { - UCHAR* p = (UCHAR*) record + key->skd_offset; + UCHAR* p = (UCHAR*) record + key->getSkdOffset(); USHORT* wp = (USHORT*) p; SORTP* lwp = (SORTP*) p; USHORT complement = key->skd_flags & SKD_descending; - USHORT n = ROUNDUP(key->skd_length, sizeof(SLONG)); + USHORT n = ROUNDUP(key->getSkdLength(), sizeof(SLONG)); // This trick replaces possibly negative zero with positive zero, so that both // would be transformed into the same sort key and thus properly compared (see CORE-3547). @@ -1039,27 +1082,71 @@ void Sort::diddleKey(UCHAR* record, bool direction) break; #endif // IEEE + case SKD_dec64: + if (direction) + { + ((Decimal64*) p)->makeKey(lwp); + p[3] ^= 1 << 7; + } + else + { + if (complement && n) + { + UCHAR* pp = p; + do { + *pp++ ^= -1; + } while (--n); + } + + p[3] ^= 1 << 7; + ((Decimal64*) p)->grabKey(lwp); + } + break; + + case SKD_dec128: + if (direction) + { + ((Decimal128*) p)->makeKey(lwp); + p[3] ^= 1 << 7; + } + else + { + if (complement && n) + { + UCHAR* pp = p; + do { + *pp++ ^= -1; + } while (--n); + } + + p[3] ^= 1 << 7; + ((Decimal128*) p)->grabKey(lwp); + } + break; + default: fb_assert(false); break; } if (complement && n) + { do { *p++ ^= -1; } while (--n); + } // Flatter but don't complement control info for non-fixed // data types when restoring the data if (key->skd_dtype == SKD_varying && !direction) { - p = (UCHAR*) record + key->skd_offset; + p = (UCHAR*) record + key->getSkdOffset(); ((vary*) p)->vary_length = *((USHORT*) (record + key->skd_vary_offset)); } if (key->skd_dtype == SKD_cstring && !direction) { - p = (UCHAR*) record + key->skd_offset; + p = (UCHAR*) record + key->getSkdOffset(); USHORT l = *((USHORT*) (record + key->skd_vary_offset)); *(p + l) = 0; } diff --git a/src/jrd/sort.h b/src/jrd/sort.h index 45d876530d..e6e95ac151 100644 --- a/src/jrd/sort.h +++ b/src/jrd/sort.h @@ -124,18 +124,6 @@ until we are out of records to sort or memory. */ -// Sort key definition block - -struct sort_key_def -{ - UCHAR skd_dtype; // Data type - UCHAR skd_flags; // Flags - USHORT skd_length; // Length if string - ULONG skd_offset; // Offset from beginning - ULONG skd_vary_offset; // Offset to varying/cstring length -}; - - // skd_dtype const int SKD_long = 1; @@ -153,12 +141,62 @@ const int SKD_cstring = 12; // non-international const int SKD_sql_time = 13; const int SKD_sql_date = 14; const int SKD_int64 = 15; +const int SKD_dec64 = 16; +const int SKD_dec128 = 17; // skd_flags const UCHAR SKD_ascending = 0; // default initializer const UCHAR SKD_descending = 1; const UCHAR SKD_binary = 2; +// Sort key definition block + +struct sort_key_def +{ + UCHAR skd_dtype; // Data type + UCHAR skd_flags; // Flags + +private: + USHORT skd_length; // Length if string + ULONG skd_offset; // Offset from beginning + +public: + ULONG skd_vary_offset; // Offset to varying/cstring length + + USHORT getSkdLength() const { return skd_length; } + + void setSkdLength(UCHAR dtype, USHORT v) + { + skd_dtype = dtype; + skd_length = v; + switch (dtype) + { + case SKD_dec64: + case SKD_dec128: + skd_length += sizeof(SLONG); + break; + } + } + + ULONG getSkdOffset() const { return skd_offset; } + + void setSkdOffset(const sort_key_def* prev = nullptr, dsc* desc = nullptr) + { + skd_offset = 0; + if (prev) + { + skd_offset = prev->skd_offset + prev->skd_length; +#ifndef WORDS_BIGENDIAN + skd_offset = ROUNDUP(skd_offset, sizeof(SLONG)); +#else + if (desc && desc->dsc_dtype >= dtype_aligned) + skd_offset = FB_ALIGN(skd_offset, type_alignments[desc->dsc_dtype]); +#endif + } + } +}; + + // Run/merge common block header struct run_merge_hdr diff --git a/src/jrd/svc.cpp b/src/jrd/svc.cpp index 98e21eea5f..14ea142796 100644 --- a/src/jrd/svc.cpp +++ b/src/jrd/svc.cpp @@ -567,8 +567,7 @@ unsigned int Service::getAuthBlock(const unsigned char** bytes) void Service::fillDpb(ClumpletWriter& dpb) { - const char* providers = "Providers=" CURRENT_ENGINE; - dpb.insertString(isc_dpb_config, providers, fb_strlen(providers)); + dpb.insertString(isc_dpb_config, EMBEDDED_PROVIDERS, fb_strlen(EMBEDDED_PROVIDERS)); if (svc_address_path.hasData()) { dpb.insertString(isc_dpb_address_path, svc_address_path); @@ -728,14 +727,20 @@ Service::Service(const TEXT* service_name, USHORT spb_length, const UCHAR* spb_d { if (svc_auth_block.hasData()) { + // remote connection - use svc_auth_block PathName dummy; RefPtr config; expandDatabaseName(svc_expected_db, dummy, &config); + Mapping mapping(Mapping::MAP_THROW_NOT_FOUND, svc_crypt_callback); + mapping.needAuthBlock(svc_auth_block); + + mapping.setAuthBlock(svc_auth_block); + mapping.setErrorMessagesContextName("services manager"); + mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr); + string trusted_role; - mapUser(true, svc_username, trusted_role, NULL, &svc_auth_block, NULL, - svc_auth_block, "services manager", NULL, config->getSecurityDatabase(), "", - svc_crypt_callback, NULL); + mapping.mapUser(svc_username, trusted_role); trusted_role.upper(); svc_trusted_role = trusted_role == ADMIN_ROLE; } diff --git a/src/jrd/tra.cpp b/src/jrd/tra.cpp index 0dd4ee3c32..0ae6e1b81d 100644 --- a/src/jrd/tra.cpp +++ b/src/jrd/tra.cpp @@ -487,7 +487,7 @@ void TRA_commit(thread_db* tdbb, jrd_tra* transaction, const bool retaining_flag status_exception::raise(&st); secContext->tra = NULL; - clearMappingCache(tdbb->getDatabase()->dbb_config->getSecurityDatabase(), MAPPING_CACHE); + Mapping::clearCache(tdbb->getDatabase()->dbb_config->getSecurityDatabase(), Mapping::MAPPING_CACHE); transaction->eraseSecDbContext(); } @@ -906,20 +906,31 @@ void TRA_update_counters(thread_db* tdbb, Database* dbb) WIN window(HEADER_PAGE_NUMBER); header_page* header = (header_page*)CCH_FETCH(tdbb, &window, LCK_write, pag_header); - if (dbb->dbb_oldest_active > header->hdr_oldest_active || - dbb->dbb_oldest_transaction > header->hdr_oldest_transaction || - dbb->dbb_oldest_snapshot > header->hdr_oldest_snapshot) + const TraNumber next_transaction = Ods::getNT(header); + const TraNumber oldest_transaction = Ods::getOIT(header); + const TraNumber oldest_active = Ods::getOAT(header); + const TraNumber oldest_snapshot = Ods::getOST(header); + + fb_assert(dbb->dbb_next_transaction <= next_transaction); + + if (dbb->dbb_oldest_active > oldest_active || + dbb->dbb_oldest_transaction > oldest_transaction || + dbb->dbb_oldest_snapshot > oldest_snapshot || + dbb->dbb_next_transaction > next_transaction) { CCH_MARK_MUST_WRITE(tdbb, &window); if (dbb->dbb_oldest_active > header->hdr_oldest_active) - header->hdr_oldest_active = dbb->dbb_oldest_active; + Ods::writeOAT(header, dbb->dbb_oldest_active); if (dbb->dbb_oldest_transaction > header->hdr_oldest_transaction) - header->hdr_oldest_transaction = dbb->dbb_oldest_transaction; + Ods::writeOIT(header, dbb->dbb_oldest_transaction); if (dbb->dbb_oldest_snapshot > header->hdr_oldest_snapshot) - header->hdr_oldest_snapshot = dbb->dbb_oldest_snapshot; + Ods::writeOST(header, dbb->dbb_oldest_snapshot); + + if (dbb->dbb_next_transaction > next_transaction) + Ods::writeNT(header, dbb->dbb_next_transaction); } CCH_RELEASE(tdbb, &window); @@ -1320,7 +1331,7 @@ void TRA_release_transaction(thread_db* tdbb, jrd_tra* transaction, Jrd::TraceTr // Release the transaction and its pool tdbb->setTransaction(NULL); - JTransaction* jTra = transaction->getInterface(); + JTransaction* jTra = transaction->getInterface(true); // ASF: maybe it's better to pass false? if (jTra) { jTra->setHandle(NULL); @@ -3442,9 +3453,9 @@ jrd_tra::~jrd_tra() } -JTransaction* jrd_tra::getInterface() +JTransaction* jrd_tra::getInterface(bool create) { - if (!tra_interface) + if (!tra_interface && create) { tra_flags |= TRA_own_interface; tra_interface = FB_NEW JTransaction(this, tra_attachment->getStable()); diff --git a/src/jrd/tra.h b/src/jrd/tra.h index 54e9fcaa85..9f37c5dd43 100644 --- a/src/jrd/tra.h +++ b/src/jrd/tra.h @@ -262,7 +262,7 @@ public: return tra_attachment->att_dsql_instance; } - JTransaction* getInterface(); + JTransaction* getInterface(bool create); void setInterface(JTransaction* jt); FB_API_HANDLE tra_public_handle; // Public handle diff --git a/src/jrd/trace/TraceManager.cpp b/src/jrd/trace/TraceManager.cpp index 00f7b915c7..fe7089a8e1 100644 --- a/src/jrd/trace/TraceManager.cpp +++ b/src/jrd/trace/TraceManager.cpp @@ -260,15 +260,19 @@ void TraceManager::update_session(const TraceSession& session) Database* dbb = attachment->att_database; fb_assert(dbb); - mapResult = mapUser(false, s_user, t_role, NULL, NULL, &priv, session.ses_auth, - attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), - dbb->dbb_config->getSecurityDatabase(), session.ses_role, - dbb->dbb_provider->getCryptCallback(), attachment->getInterface()); + Mapping mapping(Mapping::MAP_NO_FLAGS, dbb->dbb_provider->getCryptCallback()); + mapping.needSystemPrivileges(priv); + mapping.setAuthBlock(session.ses_auth); + mapping.setSqlRole(session.ses_role); + mapping.setSecurityDbAlias(dbb->dbb_config->getSecurityDatabase(), dbb->dbb_filename.c_str()); + mapping.setDb(attachment->att_filename.c_str(), dbb->dbb_filename.c_str(), + attachment->getInterface()); + mapResult = mapping.mapUser(s_user, t_role); } if (session.ses_auth.hasData()) { - if (mapResult & MAPUSER_ERROR_NOT_THROWN) + if (mapResult & Mapping::MAP_ERROR_NOT_THROWN) return; // Error in mapUser() means missing context t_role.upper(); @@ -291,9 +295,13 @@ void TraceManager::update_session(const TraceSession& session) RefPtr config; expandDatabaseName(service->getExpectedDb(), dummy, &config); - if (mapUser(false, s_user, t_role, NULL, NULL, NULL, session.ses_auth, "services manager", - NULL, config->getSecurityDatabase(), session.ses_role, service->getCryptCallback(), - NULL) & MAPUSER_ERROR_NOT_THROWN) + Mapping mapping(Mapping::MAP_NO_FLAGS, service->getCryptCallback()); + mapping.setAuthBlock(session.ses_auth); + mapping.setErrorMessagesContextName("services manager"); + mapping.setSqlRole(session.ses_role); + mapping.setSecurityDbAlias(config->getSecurityDatabase(), nullptr); + + if (mapping.mapUser(s_user, t_role) & Mapping::MAP_ERROR_NOT_THROWN) { // Error in mapUser() means missing context, therefore... return; diff --git a/src/jrd/trace/TraceObjects.cpp b/src/jrd/trace/TraceObjects.cpp index 8a8ed61415..b43e819101 100644 --- a/src/jrd/trace/TraceObjects.cpp +++ b/src/jrd/trace/TraceObjects.cpp @@ -333,7 +333,7 @@ void TraceDscFromValues::fillParams() const VariableNode* var; const LiteralNode* literal; - if ((param = prm->as())) + if ((param = nodeAs(prm))) { //const impure_value* impure = m_request->getImpure(param->impureOffset) const MessageNode* message = param->message; @@ -349,18 +349,18 @@ void TraceDscFromValues::fillParams() if (param->argFlag) { const dsc* flag = EVL_expr(tdbb, m_request, param->argFlag); - if (MOV_get_long(flag, 0)) + if (MOV_get_long(tdbb, flag, 0)) desc.dsc_flags |= DSC_null; } } - else if ((var = prm->as())) + else if ((var = nodeAs(prm))) { impure_value* impure = m_request->getImpure(var->impureOffset); from_desc = &impure->vlu_desc; } - else if ((literal = prm->as())) + else if ((literal = nodeAs(prm))) from_desc = &literal->litDesc; - else if (prm->is()) + else if (nodeIs(prm)) { desc.clear(); desc.setNull(); diff --git a/src/jrd/trig.h b/src/jrd/trig.h index 3a42468d4d..359fe3f756 100644 --- a/src/jrd/trig.h +++ b/src/jrd/trig.h @@ -41,7 +41,7 @@ struct jrd_trg USHORT trg_length; const UCHAR* trg_blr; USHORT trg_flags; - UCHAR trg_ods_version; + USHORT trg_ods_version; }; /* trigger message structure */ @@ -51,7 +51,7 @@ struct trigger_msg const SCHAR* trigmsg_name; USHORT trigmsg_number; const SCHAR* trigmsg_text; - UCHAR trg_ods_version; + USHORT trg_ods_version; }; /* generator definition structure */ diff --git a/src/jrd/types.h b/src/jrd/types.h index e16f7c5c6d..63b38ea2e0 100644 --- a/src/jrd/types.h +++ b/src/jrd/types.h @@ -41,6 +41,8 @@ TYPE("DATE", blr_sql_date, nam_f_type) TYPE("TIME", blr_sql_time, nam_f_type) TYPE("INT64", blr_int64, nam_f_type) TYPE("BOOLEAN", blr_bool, nam_f_type) +TYPE("DECFLOAT(16)", blr_dec64, nam_f_type) +TYPE("DECFLOAT(34)", blr_dec128, nam_f_type) TYPE("BINARY", 0, nam_f_sub_type) TYPE("TEXT", 1, nam_f_sub_type) diff --git a/src/jrd/val.h b/src/jrd/val.h index b6b5f81c24..bf7bd94306 100644 --- a/src/jrd/val.h +++ b/src/jrd/val.h @@ -82,6 +82,8 @@ struct impure_value SLONG vlu_dbkey[2]; float vlu_float; double vlu_double; + Firebird::Decimal64 vlu_dec64; + Firebird::Decimal128 vlu_dec128; GDS_TIMESTAMP vlu_timestamp; GDS_TIME vlu_sql_time; GDS_DATE vlu_sql_date; @@ -94,6 +96,7 @@ struct impure_value void make_long(const SLONG val, const signed char scale = 0); void make_int64(const SINT64 val, const signed char scale = 0); void make_double(const double val); + void make_decimal128(const Firebird::Decimal128 val); }; // Do not use these methods where dsc_sub_type is not explicitly set to zero. @@ -127,6 +130,16 @@ inline void impure_value::make_double(const double val) this->vlu_desc.dsc_address = reinterpret_cast(&this->vlu_misc.vlu_double); } +inline void impure_value::make_decimal128(const Firebird::Decimal128 val) +{ + this->vlu_misc.vlu_dec128 = val; + this->vlu_desc.dsc_dtype = dtype_dec128; + this->vlu_desc.dsc_length = sizeof(Firebird::Decimal128); + this->vlu_desc.dsc_scale = 0; + this->vlu_desc.dsc_sub_type = 0; + this->vlu_desc.dsc_address = reinterpret_cast(&this->vlu_misc.vlu_dec128); +} + struct impure_value_ex : public impure_value { SINT64 vlux_count; diff --git a/src/jrd/vio.cpp b/src/jrd/vio.cpp index 07b4109739..74322633c3 100644 --- a/src/jrd/vio.cpp +++ b/src/jrd/vio.cpp @@ -103,7 +103,7 @@ static void check_rel_field_class(thread_db*, record_param*, SecurityClass::flag static void delete_record(thread_db*, record_param*, ULONG, MemoryPool*); static UCHAR* delete_tail(thread_db*, record_param*, ULONG, UCHAR*, const UCHAR*); static void expunge(thread_db*, record_param*, const jrd_tra*, ULONG); -static bool dfw_should_know(record_param* org_rpb, record_param* new_rpb, +static bool dfw_should_know(thread_db*, record_param* org_rpb, record_param* new_rpb, USHORT irrelevant_field, bool void_update_is_relevant = false); static void garbage_collect(thread_db*, record_param*, ULONG, RecordStack&); @@ -1445,7 +1445,6 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) jrd_rel* relation = rpb->rpb_relation; tdbb->bumpRelStats(RuntimeStatistics::RECORD_DELETES, relation->rel_id); - tdbb->bumpStats(RuntimeStatistics::RECORD_DELETES); // Special case system transaction @@ -1485,7 +1484,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_types: if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_USER_TYPES)) protect_system_table_delupd(tdbb, relation, "DELETE", true); - if (EVL_field(0, rpb->rpb_record, f_typ_sys_flag, &desc) && MOV_get_long(&desc, 0)) + if (EVL_field(0, rpb->rpb_record, f_typ_sys_flag, &desc) && MOV_get_long(tdbb, &desc, 0)) protect_system_table_delupd(tdbb, relation, "DELETE", true); break; @@ -1521,7 +1520,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_rel_id, &desc2)) { - id = MOV_get_long(&desc2, 0); + id = MOV_get_long(tdbb, &desc2, 0); if (id < (int) rel_MAX) { IBERROR(187); // msg 187 cannot delete system relations @@ -1542,11 +1541,11 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_procedures: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_prc_id, &desc2); - id = MOV_get_long(&desc2, 0); + id = MOV_get_long(tdbb, &desc2, 0); if (EVL_field(0, rpb->rpb_record, f_prc_pkg_name, &desc2)) { - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); SCL_check_package(tdbb, &desc2, SCL_drop); } @@ -1560,20 +1559,20 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_charsets: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_cs_cs_name, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); SCL_check_charset(tdbb, object_name, SCL_drop); break; case rel_collations: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_coll_cs_id, &desc2); - id = MOV_get_long(&desc2, 0); + id = MOV_get_long(tdbb, &desc2, 0); EVL_field(0, rpb->rpb_record, f_coll_id, &desc2); - id = INTL_CS_COLL_TO_TTYPE(id, MOV_get_long(&desc2, 0)); + id = INTL_CS_COLL_TO_TTYPE(id, MOV_get_long(tdbb, &desc2, 0)); EVL_field(0, rpb->rpb_record, f_coll_name, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); SCL_check_collation(tdbb, object_name, SCL_drop); DFW_post_work(transaction, dfw_delete_collation, &desc, id); break; @@ -1581,7 +1580,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_exceptions: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_xcp_name, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); SCL_check_exception(tdbb, object_name, SCL_drop); DFW_post_work(transaction, dfw_delete_exception, &desc, 0); break; @@ -1589,7 +1588,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_gens: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_gen_name, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); SCL_check_generator(tdbb, object_name, SCL_drop); DFW_post_work(transaction, dfw_delete_generator, &desc, 0); break; @@ -1600,7 +1599,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_fun_pkg_name, &desc2)) { - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); SCL_check_package(tdbb, &desc2, SCL_drop); } else @@ -1609,7 +1608,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) } EVL_field(0, rpb->rpb_record, f_fun_id, &desc2); - id = MOV_get_long(&desc2, 0); + id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_delete_function, &desc, id, package_name); Function::lookup(tdbb, id, false, true, 0); @@ -1620,10 +1619,10 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_idx_relation, &desc); SCL_check_relation(tdbb, &desc, SCL_control); EVL_field(0, rpb->rpb_record, f_idx_id, &desc2); - if ( (id = MOV_get_long(&desc2, 0)) ) + if ( (id = MOV_get_long(tdbb, &desc2, 0)) ) { MetaName relation_name; - MOV_get_metaname(&desc, relation_name); + MOV_get_metaname(tdbb, &desc, relation_name); r2 = MET_lookup_relation(tdbb, relation_name); fb_assert(r2); @@ -1650,7 +1649,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_idx_name, &desc3); MetaName index_name; - MOV_get_metaname(&desc3, index_name); + MOV_get_metaname(tdbb, &desc3, index_name); jrd_rel *partner; index_desc idx; @@ -1679,7 +1678,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) SCL_check_relation(tdbb, &desc, SCL_control); DFW_post_work(transaction, dfw_update_format, &desc, 0); EVL_field(0, rpb->rpb_record, f_rfr_fname, &desc2); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); if ( (r2 = MET_lookup_relation(tdbb, object_name)) ) { DFW_post_work(transaction, dfw_delete_rfr, &desc2, r2->rel_id); @@ -1692,7 +1691,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) protect_system_table_delupd(tdbb, relation, "DELETE"); if (EVL_field(0, rpb->rpb_record, f_arg_pkg_name, &desc2)) { - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); SCL_check_package(tdbb, &desc2, SCL_control); } else @@ -1709,7 +1708,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_prm_pkg_name, &desc2)) { - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); SCL_check_package(tdbb, &desc2, SCL_control); } else @@ -1718,7 +1717,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) } EVL_field(0, rpb->rpb_record, f_prm_name, &desc2); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); if ( (procedure = MET_lookup_procedure(tdbb, QualifiedName(object_name, package_name), true)) ) @@ -1736,7 +1735,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_fields: protect_system_table_delupd(tdbb, relation, "DELETE"); EVL_field(0, rpb->rpb_record, f_fld_name, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); SCL_check_domain(tdbb, object_name, SCL_drop); DFW_post_work(transaction, dfw_delete_field, &desc, 0); MET_change_fields(tdbb, transaction, &desc); @@ -1748,7 +1747,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) SCL_check_database(tdbb, SCL_alter); const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); const USHORT file_flags = EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) ? - MOV_get_long(&desc2, 0) : 0; + MOV_get_long(tdbb, &desc2, 0) : 0; if (file_flags & FILE_difference) { if (file_flags & FILE_backing_up) @@ -1757,7 +1756,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) DFW_post_work(transaction, dfw_delete_difference, &desc, 0); } else if (EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) && - (id = MOV_get_long(&desc2, 0))) + (id = MOV_get_long(tdbb, &desc2, 0))) { if (!(file_flags & FILE_inactive)) { @@ -1796,7 +1795,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_trg_type, &desc2)) { DFW_post_work_arg(transaction, work, &desc2, - (USHORT) MOV_get_int64(&desc2, 0), dfw_arg_trg_type); + (USHORT) MOV_get_int64(tdbb, &desc2, 0), dfw_arg_trg_type); } break; @@ -1816,7 +1815,7 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) } EVL_field(0, rpb->rpb_record, f_prv_rname, &desc); EVL_field(0, rpb->rpb_record, f_prv_o_type, &desc2); - id = MOV_get_long(&desc2, 0); + id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_grant, &desc, id); break; @@ -1891,13 +1890,13 @@ void VIO_erase(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if ((RIDS) relation->rel_id == rel_priv) { EVL_field(0, rpb->rpb_record, f_prv_rname, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); EVL_field(0, rpb->rpb_record, f_prv_grant, &desc2); - if (MOV_get_long(&desc2, 0) == WITH_GRANT_OPTION) // ADMIN option should not cause cascade + if (MOV_get_long(tdbb, &desc2, 0) == WITH_GRANT_OPTION) // ADMIN option should not cause cascade { EVL_field(0, rpb->rpb_record, f_prv_user, &desc2); MetaName revokee; - MOV_get_metaname(&desc2, revokee); + MOV_get_metaname(tdbb, &desc2, revokee); EVL_field(0, rpb->rpb_record, f_prv_priv, &desc2); const string privilege = MOV_make_string2(tdbb, &desc2, ttype_ascii); MET_revoke(tdbb, transaction, object_name, revokee, privilege); @@ -2594,6 +2593,11 @@ bool VIO_get_current(thread_db* tdbb, VIO_data(tdbb, rpb, pool); return true; + case tra_limbo: + if (!(transaction->tra_flags & TRA_ignore_limbo)) + ERR_post(Arg::Gds(isc_rec_in_limbo) << Arg::Num(rpb->rpb_transaction_nr)); + // fall thru + case tra_active: // clear lock error from status vector fb_utils::init_status(tdbb->tdbb_status_vector); @@ -2638,10 +2642,6 @@ bool VIO_get_current(thread_db* tdbb, } break; - case tra_limbo: - BUGCHECK(184); // limbo impossible - break; - default: fb_assert(false); } @@ -2803,7 +2803,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_types: if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_USER_TYPES)) protect_system_table_delupd(tdbb, relation, "UPDATE", true); - if (EVL_field(0, org_rpb->rpb_record, f_typ_sys_flag, &desc1) && MOV_get_long(&desc1, 0)) + if (EVL_field(0, org_rpb->rpb_record, f_typ_sys_flag, &desc1) && MOV_get_long(tdbb, &desc1, 0)) protect_system_table_delupd(tdbb, relation, "UPDATE", true); break; @@ -2830,7 +2830,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j check_class(tdbb, transaction, org_rpb, new_rpb, f_dat_class); EVL_field(0, org_rpb->rpb_record, f_dat_linger, &desc1); EVL_field(0, new_rpb->rpb_record, f_dat_linger, &desc2); - if (MOV_compare(&desc1, &desc2)) + if (MOV_compare(tdbb, &desc1, &desc2)) { DFW_post_work(transaction, dfw_set_linger, &desc2, 0); } @@ -2862,7 +2862,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j if (EVL_field(0, org_rpb->rpb_record, f_prc_pkg_name, &desc2)) { - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); SCL_check_package(tdbb, &desc2, SCL_alter); } else @@ -2873,10 +2873,10 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j check_class(tdbb, transaction, org_rpb, new_rpb, f_prc_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_prc_owner); - if (dfw_should_know(org_rpb, new_rpb, f_prc_desc, true)) + if (dfw_should_know(tdbb, org_rpb, new_rpb, f_prc_desc, true)) { EVL_field(0, org_rpb->rpb_record, f_prc_id, &desc2); - const USHORT id = MOV_get_long(&desc2, 0); + const USHORT id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_modify_procedure, &desc1, id, package_name); } break; @@ -2888,7 +2888,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j if (EVL_field(0, org_rpb->rpb_record, f_fun_pkg_name, &desc2)) { - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); SCL_check_package(tdbb, &desc2, SCL_alter); } else @@ -2899,10 +2899,10 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j check_class(tdbb, transaction, org_rpb, new_rpb, f_fun_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_fun_owner); - if (dfw_should_know(org_rpb, new_rpb, f_fun_desc, true)) + if (dfw_should_know(tdbb, org_rpb, new_rpb, f_fun_desc, true)) { EVL_field(0, org_rpb->rpb_record, f_fun_id, &desc2); - const USHORT id = MOV_get_long(&desc2, 0); + const USHORT id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_modify_function, &desc1, id, package_name); } break; @@ -2910,7 +2910,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_gens: protect_system_table_delupd(tdbb, relation, "UPDATE"); EVL_field(0, org_rpb->rpb_record, f_gen_name, &desc1); - MOV_get_metaname(&desc1, object_name); + MOV_get_metaname(tdbb, &desc1, object_name); SCL_check_generator(tdbb, object_name, SCL_alter); check_class(tdbb, transaction, org_rpb, new_rpb, f_gen_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_gen_owner); @@ -2925,15 +2925,15 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j bool rc1 = EVL_field(NULL, org_rpb->rpb_record, f_rfr_null_flag, &desc1); - if ((!rc1 || MOV_get_long(&desc1, 0) == 0)) + if ((!rc1 || MOV_get_long(tdbb, &desc1, 0) == 0)) { dsc desc3, desc4; bool rc2 = EVL_field(NULL, new_rpb->rpb_record, f_rfr_null_flag, &desc2); bool rc3 = EVL_field(NULL, org_rpb->rpb_record, f_rfr_sname, &desc3); bool rc4 = EVL_field(NULL, new_rpb->rpb_record, f_rfr_sname, &desc4); - if ((rc2 && MOV_get_long(&desc2, 0) != 0) || - (rc3 && rc4 && MOV_compare(&desc3, &desc4) != 0)) + if ((rc2 && MOV_get_long(tdbb, &desc2, 0) != 0) || + (rc3 && rc4 && MOV_compare(tdbb, &desc3, &desc4) != 0)) { EVL_field(0, new_rpb->rpb_record, f_rfr_rname, &desc1); EVL_field(0, new_rpb->rpb_record, f_rfr_id, &desc2); @@ -2941,7 +2941,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j DeferredWork* work = DFW_post_work(transaction, dfw_check_not_null, &desc1, 0); SortedArray& ids = DFW_get_ids(work); - int id = MOV_get_long(&desc2, 0); + int id = MOV_get_long(tdbb, &desc2, 0); FB_SIZE_T pos; if (!ids.find(id, pos)) ids.insert(pos, id); @@ -2953,10 +2953,10 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_fields: protect_system_table_delupd(tdbb, relation, "UPDATE"); EVL_field(0, org_rpb->rpb_record, f_fld_name, &desc1); - MOV_get_metaname(&desc1, object_name); + MOV_get_metaname(tdbb, &desc1, object_name); SCL_check_domain(tdbb, object_name, SCL_alter); - if (dfw_should_know(org_rpb, new_rpb, f_fld_desc, true)) + if (dfw_should_know(tdbb, org_rpb, new_rpb, f_fld_desc, true)) { MET_change_fields(tdbb, transaction, &desc1); EVL_field(0, new_rpb->rpb_record, f_fld_name, &desc2); @@ -2973,7 +2973,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j // and hence it can be used only by a single field and therefore one relation. rc1 = EVL_field(0, org_rpb->rpb_record, f_fld_computed, &desc3); rc2 = EVL_field(0, new_rpb->rpb_record, f_fld_computed, &desc4); - if (rc1 != rc2 || rc1 && MOV_compare(&desc3, &desc4)) { + if (rc1 != rc2 || rc1 && MOV_compare(tdbb, &desc3, &desc4)) { DFW_post_work_arg(transaction, dw, &desc1, 0, dfw_arg_force_computed); } } @@ -2984,7 +2984,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j rc1 = EVL_field(NULL, org_rpb->rpb_record, f_fld_null_flag, &desc3); rc2 = EVL_field(NULL, new_rpb->rpb_record, f_fld_null_flag, &desc4); - if ((!rc1 || MOV_get_long(&desc3, 0) == 0) && rc2 && MOV_get_long(&desc4, 0) != 0) + if ((!rc1 || MOV_get_long(tdbb, &desc3, 0) == 0) && rc2 && MOV_get_long(tdbb, &desc4, 0) != 0) DFW_post_work_arg(transaction, dw, &desc2, 0, dfw_arg_field_not_null); } check_class(tdbb, transaction, org_rpb, new_rpb, f_fld_class); @@ -3004,7 +3004,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j EVL_field(0, new_rpb->rpb_record, f_idx_relation, &desc1); SCL_check_relation(tdbb, &desc1, SCL_control, false); - if (dfw_should_know(org_rpb, new_rpb, f_idx_desc, true)) + if (dfw_should_know(tdbb, org_rpb, new_rpb, f_idx_desc, true)) { EVL_field(0, new_rpb->rpb_record, f_idx_name, &desc1); @@ -3027,7 +3027,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j EVL_field(0, new_rpb->rpb_record, f_trg_rname, &desc1); SCL_check_relation(tdbb, &desc1, SCL_control); - if (dfw_should_know(org_rpb, new_rpb, f_trg_desc, true)) + if (dfw_should_know(tdbb, org_rpb, new_rpb, f_trg_desc, true)) { EVL_field(0, new_rpb->rpb_record, f_trg_rname, &desc1); DFW_post_work(transaction, dfw_update_format, &desc1, 0); @@ -3042,7 +3042,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j if (EVL_field(0, new_rpb->rpb_record, f_trg_type, &desc2)) { DFW_post_work_arg(transaction, dw, &desc2, - (USHORT) MOV_get_int64(&desc2, 0), dfw_arg_trg_type); + (USHORT) MOV_get_int64(tdbb, &desc2, 0), dfw_arg_trg_type); } } break; @@ -3054,9 +3054,9 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j SSHORT new_rel_flags, old_rel_flags; EVL_field(0, new_rpb->rpb_record, f_file_name, &desc1); if (EVL_field(0, new_rpb->rpb_record, f_file_flags, &desc2) && - ((new_rel_flags = MOV_get_long(&desc2, 0)) & FILE_difference) && + ((new_rel_flags = MOV_get_long(tdbb, &desc2, 0)) & FILE_difference) && EVL_field(0, org_rpb->rpb_record, f_file_flags, &desc2) && - ((old_rel_flags = MOV_get_long(&desc2, 0)) != new_rel_flags)) + ((old_rel_flags = MOV_get_long(tdbb, &desc2, 0)) != new_rel_flags)) { DFW_post_work(transaction, (new_rel_flags & FILE_backing_up ? dfw_begin_backup : dfw_end_backup), @@ -3068,7 +3068,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_charsets: protect_system_table_delupd(tdbb, relation, "UPDATE"); EVL_field(0, new_rpb->rpb_record, f_cs_cs_name, &desc1); - MOV_get_metaname(&desc1, object_name); + MOV_get_metaname(tdbb, &desc1, object_name); SCL_check_charset(tdbb, object_name, SCL_alter); check_class(tdbb, transaction, org_rpb, new_rpb, f_cs_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_cs_owner); @@ -3077,7 +3077,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_collations: protect_system_table_delupd(tdbb, relation, "UPDATE"); EVL_field(0, new_rpb->rpb_record, f_coll_name, &desc1); - MOV_get_metaname(&desc1, object_name); + MOV_get_metaname(tdbb, &desc1, object_name); SCL_check_collation(tdbb, object_name, SCL_alter); check_class(tdbb, transaction, org_rpb, new_rpb, f_coll_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_coll_owner); @@ -3086,7 +3086,7 @@ void VIO_modify(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, j case rel_exceptions: protect_system_table_delupd(tdbb, relation, "UPDATE"); EVL_field(0, new_rpb->rpb_record, f_xcp_name, &desc1); - MOV_get_metaname(&desc1, object_name); + MOV_get_metaname(tdbb, &desc1, object_name); SCL_check_exception(tdbb, object_name, SCL_alter); check_class(tdbb, transaction, org_rpb, new_rpb, f_xcp_class); check_owner(tdbb, transaction, org_rpb, new_rpb, f_xcp_owner); @@ -3457,7 +3457,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { if (!tdbb->getAttachment()->locksmith(tdbb, CREATE_USER_TYPES)) protect_system_table_insert(tdbb, request, relation, true); - else if (EVL_field(0, rpb->rpb_record, f_typ_sys_flag, &desc) && MOV_get_long(&desc, 0)) + else if (EVL_field(0, rpb->rpb_record, f_typ_sys_flag, &desc) && MOV_get_long(tdbb, &desc, 0)) protect_system_table_insert(tdbb, request, relation, true); } break; @@ -3498,7 +3498,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_prc_name, &desc); if (EVL_field(0, rpb->rpb_record, f_prc_pkg_name, &desc2)) - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); object_id = set_metadata_id(tdbb, rpb->rpb_record, f_prc_id, drq_g_nxt_prc_id, "RDB$PROCEDURES"); @@ -3507,7 +3507,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { // scope bool check_blr = true; if (EVL_field(0, rpb->rpb_record, f_prc_valid_blr, &desc2)) - check_blr = MOV_get_long(&desc2, 0) != 0; + check_blr = MOV_get_long(tdbb, &desc2, 0) != 0; if (check_blr) DFW_post_work_arg(transaction, work, NULL, 0, dfw_arg_check_blr); @@ -3528,7 +3528,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) EVL_field(0, rpb->rpb_record, f_fun_name, &desc); if (EVL_field(0, rpb->rpb_record, f_fun_pkg_name, &desc2)) - MOV_get_metaname(&desc2, package_name); + MOV_get_metaname(tdbb, &desc2, package_name); object_id = set_metadata_id(tdbb, rpb->rpb_record, f_fun_id, drq_g_nxt_fun_id, "RDB$FUNCTIONS"); @@ -3537,7 +3537,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { // scope bool check_blr = true; if (EVL_field(0, rpb->rpb_record, f_fun_valid_blr, &desc2)) - check_blr = MOV_get_long(&desc2, 0) != 0; + check_blr = MOV_get_long(tdbb, &desc2, 0) != 0; if (check_blr) DFW_post_work_arg(transaction, work, NULL, 0, dfw_arg_check_blr); @@ -3585,7 +3585,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_fields: EVL_field(0, rpb->rpb_record, f_fld_name, &desc); - MOV_get_metaname(&desc, object_name); + MOV_get_metaname(tdbb, &desc, object_name); SCL_check_domain(tdbb, object_name, SCL_create); DFW_post_work(transaction, dfw_create_field, &desc, 0); set_system_flag(tdbb, rpb->rpb_record, f_fld_sys_flag); @@ -3606,10 +3606,10 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { const bool name_defined = EVL_field(0, rpb->rpb_record, f_file_name, &desc); if (EVL_field(0, rpb->rpb_record, f_file_shad_num, &desc2) && - MOV_get_long(&desc2, 0)) + MOV_get_long(tdbb, &desc2, 0)) { EVL_field(0, rpb->rpb_record, f_file_flags, &desc2); - if (!(MOV_get_long(&desc2, 0) & FILE_inactive)) { + if (!(MOV_get_long(tdbb, &desc2, 0) & FILE_inactive)) { DFW_post_work(transaction, dfw_add_shadow, &desc, 0); } } @@ -3617,7 +3617,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) { USHORT rel_flags; if (EVL_field(0, rpb->rpb_record, f_file_flags, &desc2) && - ((rel_flags = MOV_get_long(&desc2, 0)) & FILE_difference)) + ((rel_flags = MOV_get_long(tdbb, &desc2, 0)) & FILE_difference)) { if (name_defined) { DFW_post_work(transaction, dfw_add_difference, &desc, 0); @@ -3653,7 +3653,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_trg_type, &desc2)) { DFW_post_work_arg(transaction, work, &desc2, - (USHORT) MOV_get_int64(&desc2, 0), dfw_arg_trg_type); + (USHORT) MOV_get_int64(tdbb, &desc2, 0), dfw_arg_trg_type); } set_system_flag(tdbb, rpb->rpb_record, f_trg_sys_flag); break; @@ -3662,7 +3662,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) protect_system_table_insert(tdbb, request, relation); EVL_field(0, rpb->rpb_record, f_prv_rname, &desc); EVL_field(0, rpb->rpb_record, f_prv_o_type, &desc2); - object_id = MOV_get_long(&desc2, 0); + object_id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_grant, &desc, object_id); break; @@ -3674,7 +3674,7 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) if (EVL_field(0, rpb->rpb_record, f_vrl_vname, &desc) && EVL_field(0, rpb->rpb_record, f_vrl_context, &desc2)) { - const USHORT id = MOV_get_long(&desc2, 0); + const USHORT id = MOV_get_long(tdbb, &desc2, 0); DFW_post_work(transaction, dfw_store_view_context_type, &desc, id); } } @@ -3741,10 +3741,10 @@ void VIO_store(thread_db* tdbb, record_param* rpb, jrd_tra* transaction) case rel_collations: { EVL_field(0, rpb->rpb_record, f_coll_cs_id, &desc); - USHORT id = MOV_get_long(&desc, 0); + USHORT id = MOV_get_long(tdbb, &desc, 0); EVL_field(0, rpb->rpb_record, f_coll_id, &desc); - id = INTL_CS_COLL_TO_TTYPE(id, MOV_get_long(&desc, 0)); + id = INTL_CS_COLL_TO_TTYPE(id, MOV_get_long(tdbb, &desc, 0)); EVL_field(0, rpb->rpb_record, f_coll_name, &desc); DFW_post_work(transaction, dfw_create_collation, &desc, id); @@ -3999,7 +3999,10 @@ bool VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction) case PREPARE_LOCKERR: // We got some kind of locking error (deadlock, timeout or lock_conflict) // Error details should be stuffed into status vector at this point - ERR_post(Arg::Gds(isc_concurrent_transaction) << Arg::Num(org_rpb->rpb_transaction_nr)); + // hvlad: we have no details as TRA_wait has already cleared the status vector + ERR_post(Arg::Gds(isc_deadlock) << + Arg::Gds(isc_update_conflict) << + Arg::Gds(isc_concurrent_transaction) << Arg::Num(org_rpb->rpb_transaction_nr)); } // Old record was restored and re-fetched for write. Now replace it. @@ -4010,7 +4013,8 @@ bool VIO_writelock(thread_db* tdbb, record_param* org_rpb, jrd_tra* transaction) org_rpb->rpb_b_line = temp.rpb_line; org_rpb->rpb_address = new_rpb.rpb_address; org_rpb->rpb_length = new_rpb.rpb_length; - org_rpb->rpb_flags |= rpb_delta; + org_rpb->rpb_flags &= ~(rpb_delta | rpb_uk_modified); + org_rpb->rpb_flags |= new_rpb.rpb_flags & rpb_delta; replace_record(tdbb, org_rpb, &stack, transaction); @@ -4138,7 +4142,7 @@ static void check_class(thread_db* tdbb, DSC desc1, desc2; EVL_field(0, old_rpb->rpb_record, id, &desc1); EVL_field(0, new_rpb->rpb_record, id, &desc2); - if (!MOV_compare(&desc1, &desc2)) + if (!MOV_compare(tdbb, &desc1, &desc2)) return; DFW_post_work(transaction, dfw_compute_security, &desc2, 0); @@ -4184,7 +4188,7 @@ static bool check_nullify_source(thread_db* tdbb, record_param* org_rpb, record_ } } - if (org_null != new_null || MOV_compare(&org_desc, &new_desc)) + if (org_null != new_null || MOV_compare(tdbb, &org_desc, &new_desc)) return false; } @@ -4213,7 +4217,7 @@ static void check_owner(thread_db* tdbb, DSC desc1, desc2; EVL_field(0, old_rpb->rpb_record, id, &desc1); EVL_field(0, new_rpb->rpb_record, id, &desc2); - if (!MOV_compare(&desc1, &desc2)) + if (!MOV_compare(tdbb, &desc1, &desc2)) return; const Jrd::Attachment* const attachment = tdbb->getAttachment(); @@ -4221,7 +4225,7 @@ static void check_owner(thread_db* tdbb, { const Firebird::MetaName name(attachment->att_user->getUserName()); desc2.makeText((USHORT) name.length(), CS_METADATA, (UCHAR*) name.c_str()); - if (!MOV_compare(&desc1, &desc2)) + if (!MOV_compare(tdbb, &desc1, &desc2)) return; } @@ -4403,7 +4407,7 @@ static UCHAR* delete_tail(thread_db* tdbb, // recreate index after field type change). So we must return true // if relevant field changed or if no fields changed. Or we must // return false if only irrelevant field changed. -static bool dfw_should_know(record_param* org_rpb, record_param* new_rpb, +static bool dfw_should_know(thread_db* tdbb, record_param* org_rpb, record_param* new_rpb, USHORT irrelevant_field, bool void_update_is_relevant) { dsc desc2, desc3; @@ -4412,7 +4416,7 @@ static bool dfw_should_know(record_param* org_rpb, record_param* new_rpb, { const bool a = EVL_field(0, org_rpb->rpb_record, iter, &desc2); const bool b = EVL_field(0, new_rpb->rpb_record, iter, &desc3); - if (a != b || MOV_compare(&desc2, &desc3)) + if (a != b || MOV_compare(tdbb, &desc2, &desc3)) { if (iter != irrelevant_field) return true; @@ -5699,11 +5703,14 @@ static int prepare_update( thread_db* tdbb, Arg::Gds(isc_update_conflict) << Arg::Gds(isc_concurrent_transaction) << Arg::Num(update_conflict_trans)); } - case tra_active: - return PREPARE_LOCKERR; case tra_limbo: - ERR_post(Arg::Gds(isc_deadlock) << Arg::Gds(isc_trainlim)); + if (!(transaction->tra_flags & TRA_ignore_limbo)) + ERR_post(Arg::Gds(isc_rec_in_limbo) << Arg::Num(rpb->rpb_transaction_nr)); + // fall thru + + case tra_active: + return PREPARE_LOCKERR; case tra_dead: break; @@ -5988,7 +5995,7 @@ static void refresh_fk_fields(thread_db* tdbb, Record* old_rec, record_param* cu // If field was not changed by user - pick up possible modification by // system cascade trigger if (flag_old == flag_new && - (!flag_old || flag_old && MOV_compare(&desc1, &desc2) == 0)) + (!flag_old || flag_old && MOV_compare(tdbb, &desc1, &desc2) == 0)) { const bool flag_tmp = EVL_field(relation, cur_rpb->rpb_record, fld, &desc1); if (flag_tmp) @@ -6017,7 +6024,7 @@ static SSHORT set_metadata_id(thread_db* tdbb, Record* record, USHORT field_id, dsc desc1; if (EVL_field(0, record, field_id, &desc1)) - return MOV_get_long(&desc1, 0); + return MOV_get_long(tdbb, &desc1, 0); SSHORT value = (SSHORT) DYN_UTIL_gen_unique_id(tdbb, dyn_id, name); dsc desc2; diff --git a/src/lock/print.cpp b/src/lock/print.cpp index e654fe2ffd..12b27cec13 100644 --- a/src/lock/print.cpp +++ b/src/lock/print.cpp @@ -360,7 +360,7 @@ int CLIB_ROUTINE main( int argc, char *argv[]) exit(FINI_OK); } SCHAR c; - while (c = *p++) + while ((c = *p++)) switch (c) { case '?': diff --git a/src/misc/pascal/Pascal.interface.pas b/src/misc/pascal/Pascal.interface.pas index e3e665fb03..17babbee1a 100644 --- a/src/misc/pascal/Pascal.interface.pas +++ b/src/misc/pascal/Pascal.interface.pas @@ -15,6 +15,8 @@ ISC_DATE = Integer; ISC_TIME = Integer; ISC_QUAD = array [1..2] of Integer; + FB_DEC16 = array [1..1] of Int64; + FB_DEC34 = array [1..2] of Int64; ntrace_relation_t = Integer; TraceCounts = Record diff --git a/src/misc/writeBuildNum.sh b/src/misc/writeBuildNum.sh index f4d0686241..b64176978b 100755 --- a/src/misc/writeBuildNum.sh +++ b/src/misc/writeBuildNum.sh @@ -9,7 +9,7 @@ BuildType=T MajorVer=4 MinorVer=0 RevNo=0 -BuildNum=582 +BuildNum=686 NowAt=`pwd` cd `dirname $0` @@ -20,8 +20,8 @@ Root=`dirname $Root` if [ "$SPECIAL_BUILD_SUFFIX" = "" ]; then # Normal builds -SuffixKind="Unstable" -SuffixVer="" +SuffixKind="Alpha" +SuffixVer="1" BuildSuffix="Firebird 4.0" [ "$SuffixKind" = "" ] || BuildSuffix="$BuildSuffix $SuffixKind" [ "$SuffixVer" = "" ] || BuildSuffix="$BuildSuffix $SuffixVer" @@ -94,10 +94,11 @@ eof createMakeVersion() { +TmpDir="${TMPDIR:-/tmp}" OdsH="${Root}/src/jrd/ods.h" -Mini="/tmp/miniods.h" -TestCpp="/tmp/test.cpp" -AOut="/tmp/a.out" +Mini="${TmpDir}/miniods.h" +TestCpp="${TmpDir}/test.cpp" +AOut="${TmpDir}/a.out" grep ODS_VERSION $OdsH | grep -v ENCODE_ODS >$Mini diff --git a/src/msgs/facilities2.sql b/src/msgs/facilities2.sql index b7e2149d6c..fe9b3794e2 100644 --- a/src/msgs/facilities2.sql +++ b/src/msgs/facilities2.sql @@ -1,7 +1,7 @@ /* MAX_NUMBER is the next number to be used, always one more than the highest message number. */ set bulk_insert INSERT INTO FACILITIES (LAST_CHANGE, FACILITY, FAC_CODE, MAX_NUMBER) VALUES (?, ?, ?, ?); -- -('2017-02-24 22:00:00', 'JRD', 0, 818) +('2017-05-23 16:08:00', 'JRD', 0, 824) ('2015-03-17 18:33:00', 'QLI', 1, 533) ('2015-01-07 18:01:51', 'GFIX', 3, 134) ('1996-11-07 13:39:40', 'GPRE', 4, 1) diff --git a/src/msgs/messages2.sql b/src/msgs/messages2.sql index 555363cf9a..1cc8b62e52 100644 --- a/src/msgs/messages2.sql +++ b/src/msgs/messages2.sql @@ -549,7 +549,7 @@ without specifying a character set.', NULL); ('exception_array_bounds_exceeded', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 450, NULL, 'Array bounds exceeded. The code attempted to access an array element that is out of bounds.', NULL, NULL); ('exception_float_denormal_operand', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 451, NULL, 'Float denormal operand. One of the floating-point operands is too small to represent a standard float value.', NULL, NULL); ('exception_float_divide_by_zero', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 452, NULL, 'Floating-point divide by zero. The code attempted to divide a floating-point value by zero.', NULL, NULL); -('exception_float_inexact_result', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 453, NULL, 'Floating-point inexact result. The result of a floating-point operation cannot be represented as a deciaml fraction.', NULL, NULL); +('exception_float_inexact_result', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 453, NULL, 'Floating-point inexact result. The result of a floating-point operation cannot be represented as a decimal fraction.', NULL, NULL); ('exception_float_invalid_operand', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 454, NULL, 'Floating-point invalid operand. An indeterminant error occurred during a floating-point operation.', NULL, NULL); ('exception_float_overflow', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 455, NULL, 'Floating-point overflow. The exponent of a floating-point operation is greater than the magnitude allowed.', NULL, NULL); ('exception_float_stack_check', 'POST_EXCEPTIONS and POST_EXTRENA', 'common.h', NULL, 0, 456, NULL, 'Floating-point stack check. The stack overflowed or underflowed as the result of a floating-point operation.', NULL, NULL); @@ -925,6 +925,12 @@ Data source : @4', NULL, NULL) ('overriding_system_invalid', NULL, 'StmtNodes.cpp', NULL, 0, 815, NULL, 'OVERRIDING SYSTEM VALUE can be used only for identity column defined as ''GENERATED ALWAYS'' in INSERT for table/view @1', NULL, NULL); ('overriding_user_invalid', NULL, 'StmtNodes.cpp', NULL, 0, 816, NULL, 'OVERRIDING USER VALUE can be used only for identity column defined as ''GENERATED BY DEFAULT'' in INSERT for table/view @1', NULL, NULL); ('overriding_system_missing', NULL, 'StmtNodes.cpp', NULL, 0, 817, NULL, 'OVERRIDING SYSTEM VALUE should be used to override the value of an identity column defined as ''GENERATED ALWAYS'' in table/view @1', NULL, NULL); +('decprecision_err', NULL, 'dsql parse.y', NULL, 0, 818, NULL, 'DecFloat precision must be 16 or 34', NULL, NULL); +('decfloat_divide_by_zero', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 819, NULL, 'Decimal float divide by zero. The code attempted to divide a DECFLOAT value by zero.', NULL, NULL); +('decfloat_inexact_result', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 820, NULL, 'Decimal float inexact result. The result of an operation cannot be represented as a decimal fraction.', NULL, NULL); +('decfloat_invalid_operation', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 821, NULL, 'Decimal float invalid operation. An indeterminant error occurred during an operation.', NULL, NULL); +('decfloat_overflow', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 822, NULL, 'Decimal float overflow. The exponent of a result is greater than the magnitude allowed.', NULL, NULL); +('decfloat_underflow', 'DecimalContext::checkForExceptions', 'DecFloat.cpp', NULL, 0, 823, NULL, 'Decimal float underflow. The exponent of a result is less than the magnitude allowed.', NULL, NULL); -- QLI (NULL, NULL, NULL, NULL, 1, 0, NULL, 'expected type', NULL, NULL); (NULL, NULL, NULL, NULL, 1, 1, NULL, 'bad block type', NULL, NULL); diff --git a/src/msgs/system_errors2.sql b/src/msgs/system_errors2.sql index 00a86ba11f..d8a425cd72 100644 --- a/src/msgs/system_errors2.sql +++ b/src/msgs/system_errors2.sql @@ -824,6 +824,12 @@ set bulk_insert INSERT INTO SYSTEM_ERRORS (SQL_CODE, SQL_CLASS, SQL_SUBCLASS, FA (-902, '42', '000', 0, 815, 'overriding_system_invalid', NULL, NULL) (-902, '42', '000', 0, 816, 'overriding_user_invalid', NULL, NULL) (-902, '42', '000', 0, 817, 'overriding_system_missing', NULL, NULL) +(-842, 'HY', '104', 0, 818, 'decprecision_err', NULL, NULL) +(-901, '22', '012', 0, 819, 'decfloat_divide_by_zero', NULL, NULL) +(-901, '22', '000', 0, 820, 'decfloat_inexact_result', NULL, NULL) +(-901, '22', '000', 0, 821, 'decfloat_invalid_operation', NULL, NULL) +(-901, '22', '003', 0, 822, 'decfloat_overflow', NULL, NULL) +(-901, '22', '003', 0, 823, 'decfloat_underflow', NULL, NULL) -- GFIX (-901, '00', '000', 3, 1, 'gfix_db_name', NULL, NULL) (-901, '00', '000', 3, 2, 'gfix_invalid_sw', NULL, NULL) diff --git a/src/qli/show.epp b/src/qli/show.epp index 715678cf6e..cb26851c71 100644 --- a/src/qli/show.epp +++ b/src/qli/show.epp @@ -213,7 +213,7 @@ void SHOW_stuff( qli_syntax* node) case show_system_relations: // No need to define a buffer here; we are interested in its length. width = get_window_size(buf_len - 1); - if (dbb = (qli_dbb*) value) + if ((dbb = (qli_dbb*) value)) show_rels(dbb, sw, width); else { @@ -1116,7 +1116,7 @@ static void show_fields( qli_rel* relation, qli_fld* fields) USHORT l = symbol->sym_length; printf(" %s", symbol->sym_string); - if (symbol = field->fld_query_name) + if ((symbol = field->fld_query_name)) { l += symbol->sym_length + 3; printf(" (%s)", symbol->sym_string); diff --git a/src/remote/client/BlrFromMessage.cpp b/src/remote/client/BlrFromMessage.cpp index 1c4b81120f..214495d913 100644 --- a/src/remote/client/BlrFromMessage.cpp +++ b/src/remote/client/BlrFromMessage.cpp @@ -117,6 +117,16 @@ void BlrFromMessage::buildBlr(IMessageMetadata* metadata) dtype = dtype_text; break; + case SQL_DEC16: + appendUChar(blr_dec64); + dtype = dtype_dec64; + break; + + case SQL_DEC34: + appendUChar(blr_dec128); + dtype = dtype_dec128; + break; + case SQL_DOUBLE: appendUChar(blr_double); dtype = dtype_double; diff --git a/src/remote/client/interface.cpp b/src/remote/client/interface.cpp index d4c88583f2..b6784f6914 100644 --- a/src/remote/client/interface.cpp +++ b/src/remote/client/interface.cpp @@ -6616,7 +6616,7 @@ static void mov_dsql_message(const UCHAR* from_msg, // Safe const cast, we are going to move from it to anywhere. from.dsc_address = const_cast(from_msg) + (IPTR) from.dsc_address; to.dsc_address = to_msg + (IPTR) to.dsc_address; - CVT_move(&from, &to, move_error); + CVT_move(&from, &to, DecimalStatus(DEC_Errors), move_error); } } diff --git a/src/remote/os/win32/wnet_proto.h b/src/remote/os/win32/wnet_proto.h index ab1723d1f8..8808852a1d 100644 --- a/src/remote/os/win32/wnet_proto.h +++ b/src/remote/os/win32/wnet_proto.h @@ -26,20 +26,10 @@ #include "../common/classes/fb_string.h" -#ifdef __cplusplus -extern "C" { -#endif - - rem_port* WNET_analyze(ClntAuthBlock*, const Firebird::PathName&, const TEXT*, bool, Firebird::RefPtr*, const Firebird::PathName*); rem_port* WNET_connect(const TEXT*, struct packet*, USHORT, Firebird::RefPtr*); rem_port* WNET_reconnect(HANDLE); -#ifdef __cplusplus -} /* extern "C" */ -#endif - - #endif // REMOTE_WNET_PROTO_H diff --git a/src/remote/parser.cpp b/src/remote/parser.cpp index 9b7dbbef6a..cf7516bba5 100644 --- a/src/remote/parser.cpp +++ b/src/remote/parser.cpp @@ -30,11 +30,13 @@ #include "../jrd/align.h" #include "../common/gdsassert.h" #include "../remote/parse_proto.h" +#include "../common/DecFloat.h" #if !defined(DEV_BUILD) || (defined(DEV_BUILD) && defined(WIN_NT)) #include "../yvalve/gds_proto.h" // gds__log() #endif +using namespace Firebird; static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length); @@ -156,7 +158,7 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length) USHORT count = *blr++; count += (*blr++) << 8; - Firebird::AutoPtr format(FB_NEW rem_fmt(count)); + AutoPtr format(FB_NEW rem_fmt(count)); ULONG net_length = 0; ULONG offset = 0; @@ -286,6 +288,18 @@ static rem_fmt* parse_format(const UCHAR*& blr, size_t& blr_length) align = type_alignments[dtype_double]; break; + case blr_dec64: + desc->dsc_dtype = dtype_dec64; + desc->dsc_length = sizeof(Decimal64); + align = type_alignments[dtype_dec64]; + break; + + case blr_dec128: + desc->dsc_dtype = dtype_dec128; + desc->dsc_length = sizeof(Decimal128); + align = type_alignments[dtype_dec128]; + break; + // this case cannot occur as switch paramater is char and blr_blob // is 261. blob_ids are actually passed around as blr_quad. diff --git a/src/remote/remote.h b/src/remote/remote.h index c2fca51dfc..5e188b2e2a 100644 --- a/src/remote/remote.h +++ b/src/remote/remote.h @@ -257,6 +257,9 @@ public: { if (rbl_self && *rbl_self == this) *rbl_self = NULL; + + if (rbl_iface) + rbl_iface->release(); } static ISC_STATUS badHandle() { return isc_bad_segstr_handle; } @@ -392,6 +395,9 @@ public: { if (rrq_self && *rrq_self == this) *rrq_self = NULL; + + if (rrq_iface) + rrq_iface->release(); } Rrq* clone() const @@ -507,6 +513,12 @@ public: if (rsr_self && *rsr_self == this) *rsr_self = NULL; + if (rsr_cursor) + rsr_cursor->release(); + + if (rsr_iface) + rsr_iface->release(); + delete rsr_status; } diff --git a/src/remote/server/server.cpp b/src/remote/server/server.cpp index b1accd8efd..bd7550668c 100644 --- a/src/remote/server/server.cpp +++ b/src/remote/server/server.cpp @@ -917,6 +917,19 @@ public: if (!allowCancel) return; + if (!(port->port_flags & PORT_disconnect)) + { + PACKET packet; + packet.p_operation = op_event; + P_EVENT* p_event = &packet.p_event; + p_event->p_event_database = rdb->rdb_id; + p_event->p_event_items.cstr_length = length; + p_event->p_event_items.cstr_address = items; + p_event->p_event_rid = event->rvnt_id; + + port->send(&packet); + } + if (event->rvnt_iface) { LocalStatus ls; @@ -924,19 +937,6 @@ public: event->rvnt_iface->cancel(&status_vector); event->rvnt_iface = NULL; } - - if (port->port_flags & PORT_disconnect) - return; - - PACKET packet; - packet.p_operation = op_event; - P_EVENT* p_event = &packet.p_event; - p_event->p_event_database = rdb->rdb_id; - p_event->p_event_items.cstr_length = length; - p_event->p_event_items.cstr_address = items; - p_event->p_event_rid = event->rvnt_id; - - port->send(&packet); } int release() @@ -2966,7 +2966,10 @@ ISC_STATUS rem_port::end_blob(P_OP operation, P_RLSE * release, PACKET* sendL) blob->rbl_iface->cancel(&status_vector); if (!(status_vector.getState() & Firebird::IStatus::STATE_ERRORS)) + { + blob->rbl_iface = NULL; release_blob(blob); + } return this->send_response(sendL, 0, 0, &status_vector, false); } @@ -3045,7 +3048,10 @@ ISC_STATUS rem_port::end_request(P_RLSE * release, PACKET* sendL) requestL->rrq_iface->free(&status_vector); if (!(status_vector.getState() & Firebird::IStatus::STATE_ERRORS)) + { + requestL->rrq_iface = NULL; release_request(requestL); + } return this->send_response(sendL, 0, 0, &status_vector, true); } @@ -4743,8 +4749,8 @@ ISC_STATUS rem_port::que_events(P_EVENT * stuff, PACKET* sendL) { if (!event->rvnt_iface) { - event->rvnt_destroyed = 0; - break; + if (event->rvnt_destroyed.compareExchange(1, 0)) + break; } } @@ -5168,6 +5174,7 @@ static void release_transaction( Rtr* transaction) { Rsr* const statement = transaction->rtr_cursors.pop(); fb_assert(statement->rsr_cursor); + statement->rsr_cursor->release(); statement->rsr_cursor = NULL; } diff --git a/src/utilities/nbackup/nbackup.cpp b/src/utilities/nbackup/nbackup.cpp index faa383076b..6871fd3ea7 100644 --- a/src/utilities/nbackup/nbackup.cpp +++ b/src/utilities/nbackup/nbackup.cpp @@ -682,7 +682,15 @@ void NBackup::create_backup() } else { - backup = CreateFile(nm.c_str(), GENERIC_WRITE, FILE_SHARE_DELETE, + // See CORE-4913 and "Create File" article on MSDN: + // When an application creates a file across a network, it is better to use + // GENERIC_READ | GENERIC_WRITE for dwDesiredAccess than to use GENERIC_WRITE + // alone. The resulting code is faster, because the redirector can use the + // cache manager and send fewer SMBs with more data. This combination also + // avoids an issue where writing to a file across a network can occasionally + // return ERROR_ACCESS_DENIED. + + backup = CreateFile(nm.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_DELETE, NULL, CREATE_NEW, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL); } if (backup != INVALID_HANDLE_VALUE) diff --git a/src/utilities/ntrace/TracePluginImpl.cpp b/src/utilities/ntrace/TracePluginImpl.cpp index a3ee1b48c6..ce9c50f4ab 100644 --- a/src/utilities/ntrace/TracePluginImpl.cpp +++ b/src/utilities/ntrace/TracePluginImpl.cpp @@ -725,21 +725,12 @@ void TracePluginImpl::appendParams(ITraceParams* params) else paramtype = "smallint"; break; - case dtype_long: if (parameters->dsc_scale) paramtype.printf("integer(*, %d)", parameters->dsc_scale); else paramtype = "integer"; break; - - case dtype_double: - if (parameters->dsc_scale) - paramtype.printf("double precision(*, %d)", parameters->dsc_scale); - else - paramtype = "double precision"; - break; - case dtype_int64: if (parameters->dsc_scale) paramtype.printf("bigint(*, %d)", parameters->dsc_scale); @@ -750,6 +741,20 @@ void TracePluginImpl::appendParams(ITraceParams* params) case dtype_real: paramtype = "float"; break; + case dtype_double: + if (parameters->dsc_scale) + paramtype.printf("double precision(*, %d)", parameters->dsc_scale); + else + paramtype = "double precision"; + break; + + case dtype_dec64: + paramtype = "decfloat(16)"; + break; + case dtype_dec128: + paramtype = "decfloat(34)"; + break; + case dtype_sql_date: paramtype = "date"; break; @@ -759,9 +764,11 @@ void TracePluginImpl::appendParams(ITraceParams* params) case dtype_timestamp: paramtype = "timestamp"; break; + case dtype_dbkey: paramtype = "db_key"; break; + case dtype_boolean: paramtype = "boolean"; break; @@ -839,6 +846,14 @@ void TracePluginImpl::appendParams(ITraceParams* params) } break; + case dtype_dec64: + ((Decimal64*) parameters->dsc_address)->toString(paramvalue); + break; + + case dtype_dec128: + ((Decimal128*) parameters->dsc_address)->toString(paramvalue); + break; + case dtype_sql_date: { struct tm times; diff --git a/src/yvalve/YObjects.h b/src/yvalve/YObjects.h index 1162b4d46e..269930c4e9 100644 --- a/src/yvalve/YObjects.h +++ b/src/yvalve/YObjects.h @@ -588,6 +588,8 @@ public: unsigned setOffsets(Firebird::CheckStatusWrapper* status, Firebird::IMessageMetadata* metadata, Firebird::IOffsetsCallback* callback); Firebird::IEventBlock* createEventBlock(Firebird::CheckStatusWrapper* status, const char** events); + Firebird::IDecFloat16* getDecFloat16(Firebird::CheckStatusWrapper* status); + Firebird::IDecFloat34* getDecFloat34(Firebird::CheckStatusWrapper* status); }; } // namespace Why diff --git a/src/yvalve/keywords.cpp b/src/yvalve/keywords.cpp index adbf0e8012..113248e800 100644 --- a/src/yvalve/keywords.cpp +++ b/src/yvalve/keywords.cpp @@ -95,6 +95,7 @@ static const TOK tokens[] = {TOK_BIN_SHR, "BIN_SHR", true}, {TOK_BIN_XOR, "BIN_XOR", true}, {TOK_BINARY, "BINARY", false}, + {TOK_BIND, "BIND", true}, {TOK_BIT_LENGTH, "BIT_LENGTH", false}, {TOK_BLOB, "BLOB", false}, {TOK_BLOCK, "BLOCK", true}, @@ -124,6 +125,7 @@ static const TOK tokens[] = {TOK_COMMIT, "COMMIT", false}, {TOK_COMMITTED, "COMMITTED", true}, {TOK_COMMON, "COMMON", true}, + {TOK_COMPARE_DECFLOAT, "COMPARE_DECFLOAT", true}, {TOK_COMPUTED, "COMPUTED", true}, {TOK_CONDITIONAL, "CONDITIONAL", true}, {TOK_CONNECT, "CONNECT", false}, @@ -159,6 +161,7 @@ static const TOK tokens[] = {TOK_DAY, "DAY", false}, {TOK_DDL, "DDL", true}, {TOK_DEC, "DEC", false}, + {TOK_DECFLOAT, "DECFLOAT", true}, {TOK_DECIMAL, "DECIMAL", false}, {TOK_DECLARE, "DECLARE", false}, {TOK_DECODE, "DECODE", true}, @@ -283,10 +286,12 @@ static const TOK tokens[] = {TOK_NAME, "NAME", true}, {TOK_NAMES, "NAMES", true}, {TOK_NATIONAL, "NATIONAL", false}, + {TOK_NATIVE, "NATIVE", true}, {TOK_NATURAL, "NATURAL", false}, {TOK_NCHAR, "NCHAR", false}, {TOK_NEXT, "NEXT", true}, {TOK_NO, "NO", false}, + {TOK_NORMALIZE_DECFLOAT, "NORMALIZE_DECFLOAT", true}, {TOK_NOT, "NOT", false}, {TOK_NTH_VALUE, "NTH_VALUE", true}, {TOK_NTILE, "NTILE", true}, @@ -336,6 +341,7 @@ static const TOK tokens[] = {TOK_PRIVILEGES, "PRIVILEGES", true}, {TOK_PROCEDURE, "PROCEDURE", false}, {TOK_PROTECTED, "PROTECTED", true}, + {TOK_QUANTIZE, "QUANTIZE", true}, {TOK_RAND, "RAND", true}, {TOK_RANGE, "RANGE", true}, {TOK_RANK, "RANK", true}, @@ -441,8 +447,10 @@ static const TOK tokens[] = {TOK_TIMESTAMP, "TIMESTAMP", false}, {TOK_TIMEOUT, "TIMEOUT", true}, {TOK_TO, "TO", false}, + {TOK_TOTALORDER, "TOTALORDER", true}, {TOK_TRAILING, "TRAILING", false}, {TOK_TRANSACTION, "TRANSACTION", true}, + {TOK_TRAPS, "TRAPS", true}, {TOK_TRIGGER, "TRIGGER", false}, {TOK_TRIM, "TRIM", false}, {TOK_TRUE, "TRUE", false}, diff --git a/src/yvalve/utl.cpp b/src/yvalve/utl.cpp index ee313712d0..825d83a595 100644 --- a/src/yvalve/utl.cpp +++ b/src/yvalve/utl.cpp @@ -1057,6 +1057,128 @@ IXpbBuilder* UtilInterface::getXpbBuilder(CheckStatusWrapper* status, } } +class DecFloat16 FB_FINAL : public AutoIface > +{ +public: + // IDecFloat16 implementation + void toBcd(const FB_DEC16* from, int* sign, unsigned char* bcd, int* exp) + { + *sign = decDoubleToBCD(reinterpret_cast(from), exp, bcd); + } + + void toString(CheckStatusWrapper* status, const FB_DEC16* from, unsigned bufSize, char* buffer) + { + try + { + if (bufSize >= STRING_SIZE) + decDoubleToString(reinterpret_cast(from), buffer); + else + { + char temp[STRING_SIZE]; + decDoubleToString(reinterpret_cast(from), temp); + int len = strlen(temp); + if (len < bufSize) + strncpy(buffer, temp, bufSize); + else + { + (Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) << + Arg::Gds(isc_trunc_limits) << Arg::Num(bufSize) << Arg::Num(len)); + } + } + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } + + void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC16* to) + { + decDoubleFromBCD(reinterpret_cast(to), exp, bcd, sign ? DECFLOAT_Sign : 0); + } + + void fromString(CheckStatusWrapper* status, const char* from, FB_DEC16* to) + { + try + { + DecimalStatus decSt(DEC_Errors); + Decimal64* val = reinterpret_cast(to); + val->set(from, decSt); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } +}; + +IDecFloat16* UtilInterface::getDecFloat16(CheckStatusWrapper* status) +{ + static DecFloat16 decFloat16; + return &decFloat16; +} + +class DecFloat34 FB_FINAL : public AutoIface > +{ +public: + // IDecFloat34 implementation + void toBcd(const FB_DEC34* from, int* sign, unsigned char* bcd, int* exp) + { + *sign = decQuadToBCD(reinterpret_cast(from), exp, bcd); + } + + void toString(CheckStatusWrapper* status, const FB_DEC34* from, unsigned bufSize, char* buffer) + { + try + { + if (bufSize >= STRING_SIZE) + decQuadToString(reinterpret_cast(from), buffer); + else + { + char temp[STRING_SIZE]; + decQuadToString(reinterpret_cast(from), temp); + int len = strlen(temp); + if (len < bufSize) + strncpy(buffer, temp, bufSize); + else + { + (Arg::Gds(isc_arith_except) << Arg::Gds(isc_string_truncation) << + Arg::Gds(isc_trunc_limits) << Arg::Num(bufSize) << Arg::Num(len)); + } + } + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } + + void fromBcd(int sign, const unsigned char* bcd, int exp, FB_DEC34* to) + { + decQuadFromBCD(reinterpret_cast(to), exp, bcd, sign ? DECFLOAT_Sign : 0); + } + + void fromString(CheckStatusWrapper* status, const char* from, FB_DEC34* to) + { + try + { + DecimalStatus decSt(DEC_Errors); + Decimal128* val = reinterpret_cast(to); + val->set(from, decSt); + } + catch (const Exception& ex) + { + ex.stuffException(status); + } + } +}; + +IDecFloat34* UtilInterface::getDecFloat34(CheckStatusWrapper* status) +{ + static DecFloat34 decFloat34; + return &decFloat34; +} + unsigned UtilInterface::setOffsets(CheckStatusWrapper* status, IMessageMetadata* metadata, IOffsetsCallback* callback) {