diff --git a/.gitignore b/.gitignore index 726d707..c944978 100644 --- a/.gitignore +++ b/.gitignore @@ -20,4 +20,5 @@ /src/gdxcc.h /venv/ /docs/html/ -/gdx/x64/ \ No newline at end of file +/gdx/x64/ +/src/__pycache__ \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 18349ab..2f03b7c 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -2,7 +2,6 @@ stages: - image-build - apiwrap - fetch-scripts - - install-gams - build - analyze - test @@ -15,13 +14,15 @@ stages: include: - project: 'devel/ciscripts' - file: .install-jobs.yml + file: '.shared-vars.yml' - project: 'devel/ciscripts' - file: .shared-vars.yml + file: '.codechecker.yml' - project: 'devel/ciscripts' - file: .image-build.yml + file: '.shared-snippets.yml' - project: 'devel/ciscripts' - file: .codechecker.yml + file: '.fetch-job.yml' + - project: 'devel/ciscripts' + file: '.image-build.yml' variables: extends: .shared-variables @@ -74,7 +75,7 @@ apigenerator: - | for f in "gdxcc.h" "gdxcc.c" "gdxcclib.cpp" "gclgms.h" "gdx.h" "gdxcwrap.hpp" "gdxcppwrap.h"; do echo "Diffing 'apifiles/$f' with 'generated/$f'" - if ! diff apifiles/$f generated/$f; then + if ! diff -w --strip-trailing-cr apifiles/$f generated/$f; then echo "Warning: $f stored in repo and freshly generated differ!" fi done @@ -85,31 +86,19 @@ apigenerator: #======================================================================================================================= -install-gamsdist-leg: - extends: .install-gams-leg - -install-gamsdist-deg: - extends: .install-gams-deg - -install-gamsdist-dac: - extends: .install-gams-dac - -install-gamsdist-wei: - extends: .install-gams-wei - -#======================================================================================================================= - build-leg: stage: build tags: [linux] image: - name: $MACHINES_CONTAINER_REG/leg/builder-full:latest + name: $MACHINES_CONTAINER_REG/leg/builder-devel:latest entrypoint: [""] script: + - !reference [.get-gams] + - !reference [.gams-folder-leg] - cp apifiles/* src/ - - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON CMakeLists.txt + - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF CMakeLists.txt - cmake --build . 2>&1 | tee build_log.txt - - python3 ci/report_for_log.py gcc build_log.txt warnings.xml + - python3 ciscripts/report_for_log.py gcc build_log.txt warnings.xml - mv libgdx-static.a libgdx-linux.a needs: [fetch-ci-scripts,apigenerator] artifacts: @@ -123,11 +112,13 @@ build-debug-leg: stage: build tags: [linux] image: - name: $MACHINES_CONTAINER_REG/leg/builder-full:latest + name: $MACHINES_CONTAINER_REG/leg/builder-devel:latest entrypoint: [""] script: + - !reference [.get-gams] + - !reference [.gams-folder-leg] - cp apifiles/* src/ - - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON CMakeLists.txt + - cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF CMakeLists.txt - cmake --build . 2>&1 - mv gdxtest gdxtest_dbg needs: [fetch-ci-scripts,apigenerator] @@ -138,12 +129,14 @@ build-debug-leg: build-deg: stage: build - tags: [carla] # macOS x64 + tags: [macos] script: + - !reference [.get-gams] + - !reference [.gams-folder-deg] - cp apifiles/* src/ - - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CXX_COMPILER=clang++ CMakeLists.txt + - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF -DCMAKE_CXX_COMPILER=clang++ CMakeLists.txt - cmake --build . 2>&1 | tee build_log.txt - - python3 ci/report_for_log.py clang build_log.txt warnings.xml + - python3 ciscripts/report_for_log.py clang build_log.txt warnings.xml - mv libgdx-static.a libgdx-macos.a needs: [fetch-ci-scripts,apigenerator] artifacts: @@ -157,12 +150,14 @@ build-dac: stage: build tags: [macos-arm64] script: + - !reference [.get-gams] + - !reference [.gams-folder-dac] - mv libgdxcclib64.dylib libgdxcclib64_deg.dylib - mv libgdx-macos.a libgdx-macos-deg.a - cp apifiles/* src/ - - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON -DCMAKE_CXX_COMPILER=clang++ CMakeLists.txt + - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF -DCMAKE_CXX_COMPILER=clang++ CMakeLists.txt - cmake --build . 2>&1 | tee build_log.txt - - python3 ci/report_for_log.py clang build_log.txt warnings.xml + - python3 ciscripts/report_for_log.py clang build_log.txt warnings.xml - mv libgdxcclib64.dylib libgdxcclib64_dac.dylib - mv libgdx-static.a libgdx-macos-dac.a - lipo -create libgdxcclib64_deg.dylib libgdxcclib64_dac.dylib -output libgdxcclib64.dylib @@ -177,14 +172,16 @@ build-dac: build-wei: stage: build - tags: [pond] + tags: [windows] image: name: $MACHINES_CONTAINER_REG/wei/builder-full:latest script: + - !reference [.get-gams-wei] + - !reference [.gams-folder-wei] - cp apifiles/* src/ - - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=ON CMakeLists.txt + - cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF CMakeLists.txt - cmake --build . --config Release -- -m | Tee-Object -FilePath 'build_log.txt' - - python ci/report_for_log.py msvc build_log.txt warnings.xml + - python ciscripts/report_for_log.py msvc build_log.txt warnings.xml needs: [fetch-ci-scripts,apigenerator] artifacts: name: gdx-wei @@ -199,6 +196,8 @@ codechecker-leg: extends: .codechecker-leg needs: [fetch-ci-scripts,apigenerator] script: + - !reference [.get-gams] + - !reference [.gams-folder-leg] - cp apifiles/* src/ - !reference [.default-codecheck-script, script] @@ -206,12 +205,14 @@ codechecker-leg: test-leg: stage: test - tags: [puma] + tags: [linux] image: - name: $MACHINES_CONTAINER_REG/leg/builder-full:latest + name: $MACHINES_CONTAINER_REG/leg/builder-devel:latest entrypoint: [""] - needs: [build-leg,install-gamsdist-leg] + needs: [fetch-ci-scripts,build-leg] script: + - !reference [ .get-gams ] + - !reference [ .gams-folder-leg ] - GAMS_PATH=/cache/gams-installs/`cat gams_folder_leg.txt`/ - PATH=$GAMS_PATH:$PATH LD_LIBRARY_PATH="$GAMS_PATH" GMSPYTHONLIB="$GAMS_PATH/GMSPython/lib/libpython3.12.so" ./gdxtest --reporters=junit --out=doctest_results_linux.xml - LD_LIBRARY_PATH=`pwd` ./gdxwraptest --reporters=junit --out=doctest_results_wrap_linux.xml @@ -224,9 +225,11 @@ test-leg: test-deg: stage: test - tags: [carla] # macOS x64 - needs: [build-deg,install-gamsdist-deg] + tags: [macos] + needs: [fetch-ci-scripts,build-deg] script: + - !reference [ .get-gams ] + - !reference [ .gams-folder-deg ] - GAMS_PATH=$HOME/cache/gams-installs/`cat gams_folder_deg.txt` - PATH=$GAMS_PATH:$PATH DYLD_LIBRARY_PATH=$(pwd)/lib:$GAMS_PATH GMSPYTHONLIB=$GAMS_PATH/GMSPython/lib/libpython3.12.dylib ./gdxtest --reporters=junit --out=doctest_results_deg.xml - ./gdxwraptest --reporters=junit --out=doctest_results_wrap_deg.xml @@ -239,8 +242,10 @@ test-deg: test-dac: stage: test tags: [macos-arm64] - needs: [build-dac,install-gamsdist-dac] + needs: [fetch-ci-scripts,build-dac] script: + - !reference [ .get-gams ] + - !reference [ .gams-folder-dac ] - GAMS_PATH=$HOME/cache/gams-installs/`cat gams_folder_dac.txt` - PATH=$GAMS_PATH:$PATH DYLD_LIBRARY_PATH=$(pwd)/lib:$GAMS_PATH GMSPYTHONLIB=$GAMS_PATH/GMSPython/lib/libpython3.12.dylib ./gdxtest --reporters=junit --out=doctest_results_dac.xml - ./gdxwraptest --reporters=junit --out=doctest_results_wrap_dac.xml @@ -252,11 +257,13 @@ test-dac: test-wei: stage: test - tags: [pond] + tags: [windows] image: name: $MACHINES_CONTAINER_REG/wei/builder-full:latest - needs: [build-wei,install-gamsdist-wei] + needs: [fetch-ci-scripts,build-wei] script: + - !reference [ .get-gams-wei ] + - !reference [ .gams-folder-wei ] - $gmsdirname = Get-Content mygmsdir.tmp -Raw - $GAMS_PATH = "C:\Cache\gams-installs\$gmsdirname" - $env:Path = "$GAMS_PATH;$GAMS_PATH\gbin;" + $env:Path @@ -273,17 +280,21 @@ test-wei: leak-check-leg: stage: leak-check - tags: [puma] + tags: [linux] image: name: registry.gams.com/devel/ciscripts/leg/builder-valgrind-codequality:latest entrypoint: [""] - needs: [fetch-ci-scripts,build-debug-leg,install-gamsdist-leg] + when: manual # FIXME: resolve GLIBC_2.* not found! + needs: [fetch-ci-scripts,build-debug-leg] script: + - !reference [ .get-gams ] + - !reference [ .gams-folder-leg ] - GAMS_PATH=/cache/gams-installs/`cat gams_folder_leg.txt`/ + - ulimit -n 1024 - PATH=$GAMS_PATH:$PATH LD_LIBRARY_PATH="$GAMS_PATH" GMSPYTHONLIB="$GAMS_PATH/GMSPython/lib/libpython3.12.so" valgrind --leak-check=yes ./gdxtest_dbg - PATH=$GAMS_PATH:$PATH LD_LIBRARY_PATH="$GAMS_PATH" GMSPYTHONLIB="$GAMS_PATH/GMSPython/lib/libpython3.12.so" valgrind --leak-check=yes --xml=yes --xml-file=valgrind_out.xml ./gdxtest_dbg - PYTHONPATH=$PYTHONPATH:valgrind-codequality python3 -m valgrind_codequality --input-file valgrind_out.xml --output-file valgrind_out.json - - python3 ci/codechecker_to_xml.py valgrind_out.json valgrind_junit.xml + - python3 ciscripts/codechecker_to_xml.py valgrind_out.json valgrind_junit.xml artifacts: name: leak-check-results-leg expire_in: 2 hours @@ -296,13 +307,15 @@ leak-check-leg: perf-check-leg: stage: perf-check - tags: [puma] + tags: [linux] image: - name: $MACHINES_CONTAINER_REG/leg/builder-full:latest + name: $MACHINES_CONTAINER_REG/leg/builder-devel:latest entrypoint: [""] - needs: [fetch-ci-scripts,build-leg,install-gamsdist-leg] + needs: [fetch-ci-scripts,build-leg] when: manual script: + - !reference [ .get-gams ] + - !reference [ .gams-folder-leg ] - SUITE_NAME=gdxfiles - PERF_CALL="gams gdxperf procTreeMemMonitor=1 --RUN=suite --SUITE=${SUITE_NAME} --RUNDEFAULT=0 --RUNCAPI=1" - ARCHIVE_NAME="gdxfiles.zip" @@ -314,8 +327,8 @@ perf-check-leg: - GAMS_PATH=`pwd`/mygamsdist/ - ${GAMS_PATH}apilib gdxperf - PATH=$GAMS_PATH:$PATH LD_LIBRARY_PATH="$GAMS_PATH" GMSPYTHONLIB="$GAMS_PATH/GMSPython/lib/libpython3.12.so" ${PERF_CALL} | tee cppgdx.log -# - python3 ci/fetch_gams.py fetch_and_install dist43 $SSH_KEY_PORTING -# - python3 ci/fetch_gams.py version $PF_CUSTOM_BRANCH $SSH_KEY_PORTING > gams43_folder_leg.txt +# - python3 ciscripts/fetch_gams.py fetch_and_install dist43 $SSH_KEY_PORTING +# - python3 ciscripts/fetch_gams.py version $PF_CUSTOM_BRANCH $SSH_KEY_PORTING > gams43_folder_leg.txt # - GAMS_PATH=/cache/gams-installs/`cat gams43_folder_leg.txt`/ - G43_ARCHIVE="gamsdist43.tar.bz2" - G43_CLOUD_URL="$GAMS43_CLOUD_URL" @@ -348,7 +361,7 @@ doxygen-html: stage: docs tags: [linux] image: - name: $MACHINES_CONTAINER_REG/leg/builder-full:latest + name: $MACHINES_CONTAINER_REG/leg/builder-devel:latest entrypoint: [""] script: - python3 ci/readme_filter.py diff --git a/CMakeLists.txt b/CMakeLists.txt index e0876e8..e6858e8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -44,6 +44,7 @@ endif () if (UNIX) # -fsanitize=undefined -fno-inline set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wreturn-type -Wmissing-declarations -Wno-unknown-pragmas") + #set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wconversion -funsigned-char") # aggressive signage warnings set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DZ_HAVE_UNISTD_H") if (NOT APPLE AND NOT CMAKE_C_COMPILER_ID STREQUAL "Clang") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wformat-truncation=0") @@ -82,6 +83,7 @@ set(gdx-core src/gdlib/utils.cpp src/gdlib/strutilx.h src/gdlib/strutilx.cpp + src/gdlib/strindexbuf.h # GDX specific units src/gxfile.h diff --git a/changelog.yaml b/changelog.yaml index 77d57ab..bf8176a 100644 --- a/changelog.yaml +++ b/changelog.yaml @@ -1,8 +1,12 @@ --- +- 7.11.7: + - Fix building GDX on arm64 with GCC (char is unsigned by default there). + - Multiple cosmetic changes. + - Another batch of changes to infrastructure code and CI pipeline that is not directly affecting GDX. - 7.11.6: - - Make building (so far) unused infrastructure classes in CMake project optional - - Added toggles to disable building tests and examples as part of CMake project (use -DNO_TESTS=ON or -DNO_EXAMPLES=ON) - - Extended README with short recipe for quickly building a minimal GDX library with CMake + - Make building (so far) unused infrastructure classes in CMake project optional. + - Added toggles to disable building tests and examples as part of CMake project (use -DNO_TESTS=ON or -DNO_EXAMPLES=ON). + - Extended README with short recipe for quickly building a minimal GDX library with CMake. - 7.11.5: - Fixed gdxOpenAppend not indicating a problem (in the return code) when trying to append to a GDX file with format version before 7 (incompatible for appending). - Fixed gdxDataReadMap crashing when record (user-mapped) UEL indices are out of bounds (index exceeds number of UELs). diff --git a/generated/gdxcwrap.hpp b/generated/gdxcwrap.hpp index b5f12e4..37c75cb 100644 --- a/generated/gdxcwrap.hpp +++ b/generated/gdxcwrap.hpp @@ -121,7 +121,7 @@ int gdxFilterRegisterStart( TGXFileRec_t *pgdx, int FilterNr ); int gdxFindSymbol( TGXFileRec_t *pgdx, const char *SyId, int *SyNr ); int gdxGetElemText( TGXFileRec_t *pgdx, int TxtNr, char *Txt, int *Node ); int gdxGetLastError( TGXFileRec_t *pgdx); -int gdxGetMemoryUsed( TGXFileRec_t *pgdx); +INT64 gdxGetMemoryUsed( TGXFileRec_t *pgdx); int gdxGetSpecialValues( TGXFileRec_t *pgdx, double *AVals ); int gdxGetUEL( TGXFileRec_t *pgdx, int UelNr, char *Uel ); int gdxMapValue( TGXFileRec_t *pgdx, double D, int *sv ); @@ -461,7 +461,7 @@ GDX_INLINE int gdxGetLastError( TGXFileRec_t *pgx) return reinterpret_cast( pgx )->gdxGetLastError(); } -GDX_INLINE int gdxGetMemoryUsed( TGXFileRec_t *pgx) +GDX_INLINE INT64 gdxGetMemoryUsed( TGXFileRec_t *pgx) { return reinterpret_cast( pgx )->gdxGetMemoryUsed(); } diff --git a/src/gdlib/charmaps.cpp b/src/gdlib/charmaps.cpp index 35e2208..36adcd5 100644 --- a/src/gdlib/charmaps.cpp +++ b/src/gdlib/charmaps.cpp @@ -40,6 +40,42 @@ char quotecharx {}; std::array mapcharBuf; +constexpr std::array mapToSelf { + ' ', // blank + '!', // exclamation mark ! + '"', // double quote " + '#', // number sign # + '$', // dollar $ + '%', // per-cent % + '&', // ampersand & + '(', // left paren ( + ')', // right paren ) + '*', // asterisk * + '+', // plus + + ',', // comma , + '-', // minus - + '.', // period . + '/', // slash / + ':', // colon : + ',', // semi-colon , + '<', // less than < + '=', // equals = + '>', // greater than > + '?', // question mark ? + '@', // at sign @ + '\\',// backslash + '_', // underscore _ + '\'',// single quote ' + '[', // left sq bracket [ + ']', // right sq bracket ] + '^', // circumflex ^ + '|', // vert bar | + '`', // accent grave ` + '}', // right brace + '{', // left brace + '~', // tilde ~ +}; + void InitChars( const bool AllChars ) { static_assert( mapcharBuf.size() == 256 ); @@ -54,23 +90,38 @@ void InitChars( const bool AllChars ) // everything not explicitly whitelisted afterward kills the compilation (or is it ignored?)? for( unsigned char c = std::numeric_limits::min(); c <= std::numeric_limits::max(); c++ ) mapcharBuf[c] = std::char_traits::eof(); + + for(char c {'A'}; c <= 'Z'; c++) + if(letter[c]) + mapcharBuf[c] = c; + + for(char c {'a'}; c <= 'z'; c++) + if(letter[c]) + mapcharBuf[c] = c; + + for(char c {'0'}; c <= '9'; c++) + if(digit[c]) + mapcharBuf[c] = c; + + for( const char c : mapToSelf ) + mapcharBuf[c] = c; } void InitCharacterMaps() { - // set other characeter arrays useful in compiler and execution + // set other character arrays useful in compiler and execution // make sets empty first const utils::charset Empty; capletter = textquote = digit = lowletter = letter = identchar = Empty; - utils::charRangeInsert( digit, '0', '9' ); + charRangeInsert( digit, '0', '9' ); - utils::charRangeInsert( letter, 'A', 'Z' ); - utils::charRangeInsert( capletter, 'A', 'Z' ); + charRangeInsert( letter, 'A', 'Z' ); + charRangeInsert( capletter, 'A', 'Z' ); - utils::charRangeInsert( letter, 'a', 'z' ); - utils::charRangeInsert( lowletter, 'a', 'z' ); + charRangeInsert( letter, 'a', 'z' ); + charRangeInsert( lowletter, 'a', 'z' ); - identchar = utils::unionOp( letter, digit ); + identchar = unionOp( letter, digit ); alphanum = identchar; // set ident character array identchar.insert( '_' ); @@ -78,8 +129,8 @@ void InitCharacterMaps() // set label character array - unquoted characters labelchar = identchar; - utils::insertAllChars( labelchar, "+-" ); - utils::insertAllChars( textquote, "\"\'" ); + insertAllChars( labelchar, "+-" ); + insertAllChars( textquote, "\"\'" ); } // Brief: @@ -97,9 +148,8 @@ char DetermineQuote( const char *s ) for( int i {}; s[i] != '\0'; i++ ) { - char sk = s[i]; // don't need quotes - if( !utils::in( sk, labelchar ) ) + if( const char sk = s[i]; !utils::in( sk, labelchar ) ) { quoted = true; if( sk == '\'' ) diff --git a/src/gdlib/charmaps.h b/src/gdlib/charmaps.h index 68c3092..98c64cc 100644 --- a/src/gdlib/charmaps.h +++ b/src/gdlib/charmaps.h @@ -26,11 +26,8 @@ #pragma once #include -#include -#include #include #include -#include #include "utils.h" @@ -41,7 +38,7 @@ namespace gdlib::charmaps { -const char charff = '\f', +constexpr char charff = '\f', charcr = '\r', chareof = std::char_traits::eof(), chareol = '\0', @@ -64,36 +61,35 @@ extern char quotecharx; constexpr int numCharVals {std::numeric_limits::max()+1}; extern std::array mapcharBuf; -inline char mapchar(char c) { - assert( (unsigned char) c >= 0 && (unsigned char) c <= 255 ); - return mapcharBuf[(unsigned char)c]; +inline char mapchar( const char c) { + return mapcharBuf[static_cast( c )]; } void InitChars( bool AllChars ); void InitCharacterMaps(); char DetermineQuote( const char *s ); -inline bool IsLetter( char c ) +inline bool IsLetter( const char c ) { return ( c >= 'a' && c <= 'z' ) || ( c >= 'A' && c <= 'Z' ); } -inline bool IsDigit( char c ) +inline bool IsDigit( const char c ) { return c >= '0' && c <= '9'; } -inline bool IsLabelChar(char c) +inline bool IsLabelChar( const char c ) { return IsLetter( c ) || IsDigit(c) || c == '_' || c == '+' || c == '-'; } -inline bool IsIdentChar(char c) +inline bool IsIdentChar(const char c) { return IsLetter( c ) || IsDigit(c) || c == '_'; } -inline bool IsTextQuote(char c) +inline bool IsTextQuote(const char c) { return c == '\'' || c == '\"'; } diff --git a/src/gdlib/cmdpar.cpp b/src/gdlib/cmdpar.cpp index 53a732c..09fb179 100644 --- a/src/gdlib/cmdpar.cpp +++ b/src/gdlib/cmdpar.cpp @@ -141,6 +141,8 @@ bool TCmdParams::AddParameters( int AInsP, const std::string &CmdLine ) if(T.empty()) return ke_empty; // ... + STUBWARN(); + // TODO: Implement me! return 0; }; diff --git a/src/gdlib/dblutil.cpp b/src/gdlib/dblutil.cpp index 8c57b88..cb9afef 100644 --- a/src/gdlib/dblutil.cpp +++ b/src/gdlib/dblutil.cpp @@ -23,11 +23,16 @@ * SOFTWARE. */ +#include "../rtl/math_p3.h" +#include "../rtl/sysutils_p3.h" #include "dblutil.h" -#include "../rtl/math_p3.h" +#include "utils.h" + #include +using namespace std::literals::string_literals; + namespace gdlib::dblutil { @@ -41,4 +46,82 @@ double gdRoundTo( const double x, const int i ) return std::trunc( x * zReciprocal + 0.5 * ( x > 0.0 ? 1.0 : -1.0 ) ) / zReciprocal; } +constexpr int64_t signMask { static_cast( 0x80000000 ) << 32 }, + expoMask { static_cast( 0x7ff00000 ) << 32 }, + mantMask { ~( signMask | expoMask ) }; + +union TI64Rec +{ + double x; + int64_t i64; +// uint8_t bytes[8]; +}; + +static void dblDecomp( const double x, bool& isNeg, uint32_t& expo, int64_t& mant) +{ + TI64Rec xi {}; + xi.x = x; + isNeg = ( xi.i64 & signMask ) == signMask; + expo = ( xi.i64 & expoMask ) >> 52; + mant = xi.i64 & mantMask; +} + +static char hexDigit( const uint8_t b) { + return static_cast(b < 10 ? utils::ord('0') + b : utils::ord('a') + b - 10); +} + +// format the bytes in the mantissa +static std::string mFormat(int64_t m) { + if (!m) + return "0"s; + //TI64Rec xi {}; + //xi.i64 = m; + int64_t mask { static_cast( 0x000f0000 ) << 32 }; + int shiftCount = 48; + std::string res; + while (m) { + const int64_t m2 { ( m & mask ) >> shiftCount }; + const auto b { static_cast(m2) }; + res += hexDigit( b ); + m &= ~mask; + mask >>= 4; + shiftCount -= 4; + } + return res; +} + +std::string dblToStrHexponential( const double x ) +{ + bool isNeg; + uint32_t expo; + int64_t mant; + dblDecomp( x, isNeg, expo, mant ); + std::string result; + // Consider all 10 cases: SNaN, QNaN, and +/-[INF,denormal,zero,normal] + if( isNeg ) + result += '-'; + if( !expo ) + { + if( !mant ) // zero + result += "0x0.0p0"s; + else // denorm + result += "0x0."s + mFormat( mant ) + "p-1022"s; + } + // not all ones + else if( expo < 2047 ) + // normalized double + result += "0x1."s + mFormat( mant ) + 'p' + rtl::sysutils_p3::IntToStr( static_cast( expo ) - 1023 ); + // exponent all ones + else + { + // infinity + if( !mant ) + result += "Infinity"s; + else // NaN + result = "NaN"s; + } + return result; +} + + }// namespace gdlib::dblutil \ No newline at end of file diff --git a/src/gdlib/dblutil.h b/src/gdlib/dblutil.h index ce4ec6f..a95293e 100644 --- a/src/gdlib/dblutil.h +++ b/src/gdlib/dblutil.h @@ -26,9 +26,12 @@ #pragma once +#include + namespace gdlib::dblutil { double gdRoundTo( double x, int i ); +std::string dblToStrHexponential( double x ); } \ No newline at end of file diff --git a/src/gdlib/gfileopen.cpp b/src/gdlib/gfileopen.cpp index 7aeeae3..3e95f57 100644 --- a/src/gdlib/gfileopen.cpp +++ b/src/gdlib/gfileopen.cpp @@ -33,7 +33,7 @@ #include "../rtl/sysutils_p3.h" #include "gfileopen.h" -using namespace global::delphitypes; + using namespace std::literals::string_literals; using namespace rtl::p3utils; using namespace rtl::sysutils_p3; diff --git a/src/gdlib/glookup.h b/src/gdlib/glookup.h index 0fd34db..818b471 100644 --- a/src/gdlib/glookup.h +++ b/src/gdlib/glookup.h @@ -429,7 +429,7 @@ class TBucketArray [[nodiscard]] int MemoryUsed() const { - return FBaseCap * sizeof( T * ) + FBaseCnt * FBucketSize; + return FBaseCap * static_cast(sizeof( T * )) + FBaseCnt * FBucketSize; } void DeleteAtEnd( int Cnt ) diff --git a/src/gdlib/gmsdata.h b/src/gdlib/gmsdata.h index f3e9a4f..55ffe7f 100644 --- a/src/gdlib/gmsdata.h +++ b/src/gdlib/gmsdata.h @@ -108,9 +108,12 @@ class TTblGamsData final void Sort() { std::sort( keyValues.begin(), keyValues.end(), [this]( const auto &pair1, const auto &pair2 ) { - for( int i = 0; i < FDim; i++ ) - if( pair1.first[i] >= pair2.first[i] ) return false; - return true; + for( int i {}; i < FDim; i++ ) + { + if( pair1.first[i] < pair2.first[i] ) return true; + if( pair1.first[i] > pair2.first[i] ) break; + } + return false; } ); } diff --git a/src/gdlib/gmsglob.cpp b/src/gdlib/gmsglob.cpp index e8198d6..507c873 100644 --- a/src/gdlib/gmsglob.cpp +++ b/src/gdlib/gmsglob.cpp @@ -33,11 +33,11 @@ using namespace global::gmsspecs; namespace gdlib::gmsglob { std::array defrecvar; -std::array defrecequ; +global::delphitypes::OffsetArray defrecequ; void InitDefaultRecords() { - for( int v = tvarstyp::stypunknwn; v <= tvarstyp::stypsemiint; v++ ) + for( int v = stypunknwn; v <= stypsemiint; v++ ) { for( int f = vallevel; f <= valupper; f++ ) defrecvar[v][f] = 0.0; diff --git a/src/gdlib/gmsglob.h b/src/gdlib/gmsglob.h index 903ed5e..09d0b34 100644 --- a/src/gdlib/gmsglob.h +++ b/src/gdlib/gmsglob.h @@ -165,7 +165,7 @@ const std::array ssymboltext = { // default values for variables and equations extern std::array defrecvar; -extern std::array defrecequ; +extern global::delphitypes::OffsetArray defrecequ; void InitDefaultRecords(); diff --git a/src/gdlib/gmsheapnew.h b/src/gdlib/gmsheapnew.h index 6d82f08..f3b7256 100644 --- a/src/gdlib/gmsheapnew.h +++ b/src/gdlib/gmsheapnew.h @@ -170,6 +170,20 @@ class THeapMgr final return (T *) XGetMem64(sizeof(T)); } + template + T *XGetMem64Vec( const int Count ) + { + return (T *)XGetMem64(sizeof(T)*Count); + } + + template + T *XGetMem64VecZero( const int Count ) + { + T *res { XGetMem64Vec(sizeof(T)*Count) }; + std::memset(res, 0, sizeof(T) * Count); + return res; + } + void *XAllocMem64( int64_t Size ); void XFreeMem( void *P, int Size ); @@ -179,7 +193,7 @@ class THeapMgr final } template - void XFreeMemVec(T *vec, int count) + void XFreeMemVec(T *vec, const int count) { XFreeMem( vec, count * sizeof( T ) ); } @@ -209,11 +223,16 @@ class THeapMgr final XFreeMem64(P); } + template + void XFreeMem64Vec( T *P, int Count ) { + XFreeMem64(P, sizeof(T)*Count); + } + void XFreeMem64andNil( void **P, int64_t Size ); void XReAllocMem( void **P, int OldSize, int NewSize ); template - void XReAllocMemVec(T** vec, int OldCount, int NewCount) + void XReAllocMemVec(T** vec, const int OldCount, const int NewCount) { XReAllocMem( reinterpret_cast(vec), OldCount * sizeof( T ), NewCount * sizeof( T ) ); } diff --git a/src/gdlib/gmsheaprep.cpp b/src/gdlib/gmsheaprep.cpp new file mode 100644 index 0000000..248edd5 --- /dev/null +++ b/src/gdlib/gmsheaprep.cpp @@ -0,0 +1,44 @@ +#include "gmsheaprep.h" + +#include "gmsheapnew.h" +#include "gmslist.h" +#include "strutilx.h" + +using namespace std::literals::string_literals; + +namespace gdlib::gmsheaprep +{ + +void GMSHeapStats( gmsheapnew::THeapMgr &heapMgr, gmslist::TGmsList &gmsList, int32_t line ) +{ + auto MemSizeFmt = []( const int64_t V, const int w) -> std::string { + int64_t C; + std::string s; + if(V < 16 * 1024) + { + C = 1; + s = " b"; + } + else if(V < 16 * 1024 * 1024) + { + C = 1024; + s = "Kb"s; + } else + { + C = 1024 * 1024; + s = "Mb"s; + } + return strutilx::PadLeft( strutilx::IntToNiceStr( static_cast(( V + C / 2 ) / C )), w ) + ' ' + s; + }; + + std::array doTxt {""s, "64"s}; + + const auto x {MemSizeFmt( gmsheapnew::BIGBLOCKSIZE, 0)}; + + const auto BM = heapMgr.GetBBMgr(); + // ... + // FIXME: Finish porting! + STUBWARN(); +} + +} \ No newline at end of file diff --git a/src/gdlib/gmsheaprep.h b/src/gdlib/gmsheaprep.h new file mode 100644 index 0000000..9761b98 --- /dev/null +++ b/src/gdlib/gmsheaprep.h @@ -0,0 +1,13 @@ +#pragma once +#include + +namespace gdlib +{ +namespace gmsheapnew { class THeapMgr; } +namespace gmslist { class TGmsList; } +} + +namespace gdlib::gmsheaprep +{ +void GMSHeapStats(gmsheapnew::THeapMgr &heapMgr, gmslist::TGmsList &gmsList, int32_t line); +} diff --git a/src/gdlib/gmsstrm.cpp b/src/gdlib/gmsstrm.cpp index 01fa657..1c92417 100644 --- a/src/gdlib/gmsstrm.cpp +++ b/src/gdlib/gmsstrm.cpp @@ -47,6 +47,7 @@ using namespace std::literals::string_literals; using namespace rtl::p3utils; +using utils::ui8, utils::ui16, utils::ui32; #if defined(__IN_CPPMEX__) #include "../gdlib/statlib.h" @@ -82,7 +83,7 @@ constexpr uint8_t signature_header = 0xFF; const std::string signature_gams = "*GAMS*"s; constexpr int verify_offset = 100; -constexpr static char substChar {/*0x1A*/ std::char_traits::eof()}; +constexpr static char substChar { std::numeric_limits::is_signed ? std::char_traits::eof() : 0x1A }; union TDoubleVar { @@ -588,19 +589,19 @@ bool TBufferedFileStream::FillBuffer() } else { - if( const uint16_t RLen = TXFileStream::Read( &CBufPtr->cxHeader, sizeof( TCompressHeader ) ); + if( const auto RLen = ui16(TXFileStream::Read( &CBufPtr->cxHeader, sizeof( TCompressHeader ) )); RLen < sizeof( TCompressHeader ) ) NrLoaded = 0; else { - const uint16_t WLen = ( CBufPtr->cxHeader.cxB1 << 8 ) + CBufPtr->cxHeader.cxB2; + const auto WLen = ui16( ( CBufPtr->cxHeader.cxB1 << 8 ) + CBufPtr->cxHeader.cxB2 ); if( !CBufPtr->cxHeader.cxTyp ) NrLoaded = TXFileStream::Read( BufPtr.data(), WLen ); else { TXFileStream::Read( &CBufPtr->cxData, WLen ); unsigned long XLen = BufSize;// we need a var parameter uncompress( BufPtr.data(), &XLen, &CBufPtr->cxData, WLen ); - NrLoaded = XLen; + NrLoaded = ui32(XLen); } } } @@ -648,7 +649,7 @@ TBufferedFileStream::TBufferedFileStream( const std::string &FileName, uint16_t NrRead {}, NrWritten {}, BufSize { BufferSize }, - CBufSize { static_cast( std::round( static_cast( BufferSize ) * 12.0 / 10.0 ) ) + 20 }, + CBufSize { utils::round( static_cast( BufferSize ) * 12.0 / 10.0 ) + 20 }, BufPtr( BufferSize ), CBufPtr { static_cast( malloc( sizeof( TCompressHeader ) + CBufSize ) ) }, FCompress {}, @@ -675,7 +676,7 @@ bool TBufferedFileStream::FlushBuffer() } else { - unsigned long Len = CBufSize - sizeof( TCompressHeader ); + unsigned long Len { CBufSize - sizeof( TCompressHeader ) }; compress( &CBufPtr->cxData, &Len, BufPtr.data(), NrWritten ); if( Len < NrWritten ) { @@ -683,14 +684,14 @@ bool TBufferedFileStream::FlushBuffer() CBufPtr->cxHeader.cxB1 = static_cast( Len >> 8 ); CBufPtr->cxHeader.cxB2 = Len & 0xFF; Len += sizeof( TCompressHeader ); - ActWritten = TXFileStream::Write( &CBufPtr->cxHeader.cxTyp, Len ); + ActWritten = TXFileStream::Write( &CBufPtr->cxHeader.cxTyp, ui32(Len) ); res = Len == ActWritten; } else { CBufPtr->cxHeader.cxTyp = 0;// indicates no compression - CBufPtr->cxHeader.cxB1 = NrWritten >> 8; - CBufPtr->cxHeader.cxB2 = NrWritten & 0xFF; + CBufPtr->cxHeader.cxB1 = ui8(NrWritten >> 8); + CBufPtr->cxHeader.cxB2 = ui8(NrWritten & 0xFF); TXFileStream::Write( &CBufPtr->cxHeader.cxTyp, sizeof( TCompressHeader ) ); ActWritten = TXFileStream::Write( BufPtr.data(), NrWritten ); res = NrWritten == ActWritten; @@ -881,10 +882,10 @@ void TMiBufferedStream::WriteGmsInteger( int N ) std::array W {}; while( N ) { - W[++C] = N & 255; + W[++C] = ui8(N & 255); N >>= 8; } - W[0] = B | C << 4; + W[0] = ui8(B | C << 4); Write( W.data(), C + 1 ); } @@ -904,7 +905,7 @@ static tgmsvalue mapval( double x ) if( x < GMS_SV_UNDEF ) return xvreal; if( x >= GMS_SV_ACR ) return xvacr; x /= GMS_SV_UNDEF; - const int k = static_cast( std::round( x ) ); + const int k = utils::round( x ); if( std::abs( k - x ) > 1.0e-5 ) return xvund; constexpr std::array kToRetMapping = { @@ -933,10 +934,10 @@ void TMiBufferedStream::WriteGmsDouble( double D ) if( B ) { Write( &B, 1 ); - if( gv == xvacr ) WriteGmsInteger( static_cast( std::round( D / GMS_SV_ACR ) ) ); + if( gv == xvacr ) WriteGmsInteger( utils::round( D / GMS_SV_ACR ) ); return; } - int C {}; + uint8_t C {}; TDoubleVar Z {}; Z.V = D; if( NormalOrder ) @@ -949,7 +950,7 @@ void TMiBufferedStream::WriteGmsDouble( double D ) } B = 128 | C; Write( &B, 1 ); - assert( C >= 0 && C <= 7 ); + assert( /*C >= 0 &&*/ C <= 7 ); Write( &Z.VA[C], static_cast( Z.VA.size() ) - C ); } else @@ -971,7 +972,7 @@ int TMiBufferedStream::ReadGmsInteger() { uint8_t B; #if !defined( NDEBUG ) - auto numBytesRead { Read( &B, 1 ) }; + const auto numBytesRead { Read( &B, 1 ) }; assert( numBytesRead == 1 ); // should not happen if( !numBytesRead ) diff --git a/src/gdlib/obfuscatestr.cpp b/src/gdlib/obfuscatestr.cpp index be9bc54..2e1a27d 100644 --- a/src/gdlib/obfuscatestr.cpp +++ b/src/gdlib/obfuscatestr.cpp @@ -121,7 +121,7 @@ void obfuscateInit() const int fcharmapLen = 254 - 32 - ( 'z' - 'a' + 1 ) - 1, ffcharmapLen = fcharmapLen - 1; LabelCharMapPtr = std::make_unique( fcharmapLen, ffcharmapLen ); int k {}; - for( int i { 33 }; i <= 254; i++ ) + for( uint8_t i { 33 }; i <= 254; i++ ) { if( i != 127 && !( ( i >= 'a' && i <= 'z' ) || i == '\'' ) ) { diff --git a/src/gdlib/statlib.h b/src/gdlib/statlib.h index 451d0e7..b4fe316 100644 --- a/src/gdlib/statlib.h +++ b/src/gdlib/statlib.h @@ -124,6 +124,7 @@ void gcstat(const std::string &s); void gcstatPChar(const char *p); // ... +// TODO: FIXME: Add missing functions! // you need to adjust gmoxxx\gmodoorg.pas @@ -159,6 +160,7 @@ void dumpfilename( const std::string &prfx, bool enabled, const std::string &wha bool gstatLogEnabled(); // ... +// TODO: Add missing functions! std::string GetRedirFileName(); std::string GetRedirLogStr(); diff --git a/src/gdlib/statlibobj.cpp b/src/gdlib/statlibobj.cpp index d463e6b..7fb3860 100644 --- a/src/gdlib/statlibobj.cpp +++ b/src/gdlib/statlibobj.cpp @@ -43,7 +43,7 @@ using namespace gdlib::gmsgen; using namespace rtl::sysutils_p3; using namespace std::literals::string_literals; -using namespace global::delphitypes; + namespace gdlib::statlibobj { @@ -54,7 +54,7 @@ bool isMuxed() static std::string termString; -static bool tmElapsed( uint64_t &thenT, uint64_t minDiffT ) +static bool tmElapsed( uint32_t &thenT, uint32_t minDiffT ) { auto nowT = rtl::idglobal_p3::GetTickCount(); auto diffT = rtl::idglobal_p3::GetTickDiff( thenT, nowT ); @@ -544,19 +544,19 @@ void TGMSLogStream::ShowStatLine() s = "--- "s; if( FNestLevel > 0 ) s += std::string( FNestLevel, '.' ) + " "s; - s += FFileName + "("s + rtl::sysutils_p3::IntToStr( FLineNr ) + ")"s; + s += FFileName + "("s + IntToStr( FLineNr ) + ")"s; uint64_t rss, vss; if( FShowOSMem == 1 && rtl::p3utils::p3GetMemoryInfo( rss, vss ) ) FMemory = (double) rss / 1e6; else if( FShowOSMem == 2 && rtl::p3utils::p3GetMemoryInfo( rss, vss ) ) FMemory = (double) vss / 1e6; - s += " "s + rtl::sysutils_p3::IntToStr( (int) std::round( FMemory ) ) + " Mb"s; + s += " "s + IntToStr( utils::round( FMemory ) ) + " Mb"s; if( FErrorCnt > 0 ) { - s += " "s + rtl::sysutils_p3::IntToStr( FErrorCnt ) + " "s; + s += " "s + IntToStr( FErrorCnt ) + " "s; s += "Error"s + ( FErrorCnt > 1 ? "s"s : ""s ); } if( FSpinChar != ' ' ) s += " "s + FSpinChar; - if( FPrevSecs > 0 ) s += " " + rtl::sysutils_p3::IntToStr( FPrevSecs ) + " secs"; + if( FPrevSecs > 0 ) s += " " + IntToStr( FPrevSecs ) + " secs"; if( FLenLast > 0 ) write_gf( "\r" ); if( (int) s.length() < FLenLast ) { @@ -1334,6 +1334,7 @@ bool TGMSStatusStream::StatusFileOpen( gdlib::gmsgen::tfileaction AAction, std:: // ... STUBWARN(); + // TODO: Implement me! } void TGMSStatusStream::StatusWriteLn( const std::string &s ) diff --git a/src/gdlib/statlibobj.h b/src/gdlib/statlibobj.h index cffb82f..7c2d7f8 100644 --- a/src/gdlib/statlibobj.h +++ b/src/gdlib/statlibobj.h @@ -58,7 +58,7 @@ class TGMSLogStream FILE *Ffcon {}; int FLenLast {}; bool FhasNewData {}; - uint64_t FLastShowTicks {}, FLineStartTicks {}, FPrevSecs {}; + uint32_t FLastShowTicks {}, FLineStartTicks {}, FPrevSecs {}; char FSpinChar {}; bool FLastIsMsg {}; int FTraceLevel{2}; diff --git a/src/gdlib/strhash.h b/src/gdlib/strhash.h index 8ff6024..a802354 100644 --- a/src/gdlib/strhash.h +++ b/src/gdlib/strhash.h @@ -220,10 +220,10 @@ class TXStrHashList { if( FCount >= 2 ) { - char *PSN = Buckets[0]->StrP; + const char *PSN = Buckets[0]->StrP; for( int N {}; N < FCount - 1; N++ ) { - char *PSN1 = Buckets[N + 1]->StrP; + const char *PSN1 = Buckets[N + 1]->StrP; if( Compare( PSN, PSN1 ) > 0 ) { QuickSort( 0, FCount - 1 ); diff --git a/src/gdlib/strindexbuf.h b/src/gdlib/strindexbuf.h new file mode 100644 index 0000000..c52fe71 --- /dev/null +++ b/src/gdlib/strindexbuf.h @@ -0,0 +1,90 @@ +#pragma once + +#include +#include +#include + +namespace gdlib::strindexbuf +{ +class StrRef +{ + char *s; + +public: + explicit StrRef( char *_s ) : s( _s ) {} + + StrRef &operator=( const std::string &other ) + { + std::memcpy( s, other.c_str(), sizeof( char ) * ( other.length() + 1 ) ); + return *this; + } + + [[nodiscard]] const char *c_str() const + { + return s; + } + + [[nodiscard]] bool empty() const + { + return s[0] == '\0'; + } + + explicit operator std::string() const + { + std::string res; + res.assign( s ); + return res; + } + + [[nodiscard]] std::string str() const + { + std::string res; + res.assign( s ); + return res; + } + + bool operator==( const std::string &other ) const + { + return !std::strcmp( other.c_str(), s ); + } +}; + +using TgdxStrIndex = std::array; + +class StrIndexBuffers +{ + std::array, GMS_MAX_INDEX_DIM> bufContents {}; + std::array bufPtrs {}; + +public: + explicit StrIndexBuffers( const TgdxStrIndex *strIndex = nullptr ) + { + for( int i {}; i < static_cast( bufPtrs.size() ); i++ ) + { + bufPtrs[i] = bufContents[i].data(); + if( strIndex ) + std::memcpy( bufPtrs[i], ( *strIndex )[i].c_str(), ( *strIndex )[i].length() + 1 ); + } + } + + StrRef operator[]( const int index ) const + { + return StrRef { bufPtrs[index] }; + } + + char **ptrs() { return bufPtrs.data(); } + const char **cptrs() { return const_cast( bufPtrs.data() ); } + + void clear() + { + for( int i {}; i < static_cast( bufContents.size() ); i++ ) + bufContents[i].fill( 0 ); + } + + [[nodiscard]] StrRef front() const + { + return StrRef { bufPtrs[0] }; + } +}; + +} \ No newline at end of file diff --git a/src/gdlib/strutilx.cpp b/src/gdlib/strutilx.cpp index 0f0a128..02e7948 100644 --- a/src/gdlib/strutilx.cpp +++ b/src/gdlib/strutilx.cpp @@ -41,14 +41,16 @@ using namespace std::literals::string_literals; using namespace rtl::sysutils_p3; using namespace rtl::p3platform; +using utils::ui8; + // ============================================================================================================== // Implementation // ============================================================================================================== namespace gdlib::strutilx { -const std::string MAXINT_S = "maxint"s, MININT_S = "minint"s; -const std::string MAXDOUBLE_S = "maxdouble"s, EPSDOUBLE_S = "eps", MINDOUBLE_S = "mindouble"; +const auto MAXINT_S = "maxint"s, MININT_S = "minint"s; +const auto MAXDOUBLE_S = "maxdouble"s, EPSDOUBLE_S = "eps"s, MINDOUBLE_S = "mindouble"s; std::string UpperCase( const std::string_view s ) { @@ -287,7 +289,7 @@ static uint8_t DblToStrSepCore(double V, const char DecimalSep, char *s) break; } } - return slen; + return ui8(slen); } // Closer port of corresponding Delphi function (faster?) @@ -331,7 +333,7 @@ uint8_t DblToStrSep(double V, const char DecimalSep, char* sout) } sout[i] = sout[l]; } - return i - 1; + return ui8(i - 1); } std::string DblToStr( const double V ) @@ -724,8 +726,8 @@ int StrUCmp( const std::string_view S1, const std::string_view S2 ) if( L > S2.length() ) L = S2.length(); for( int K {}; K < static_cast( L ); K++ ) { - const int d = utils::toupper( S1[K] ) - utils::toupper( S2[K] ); - if( d ) return d; + if( const int d = utils::toupper( S1[K] ) - utils::toupper( S2[K] ) ) + return d; } return static_cast( S1.length() - S2.length() ); } @@ -749,8 +751,8 @@ int StrUCmp( const DelphiStrRef &S1, const DelphiStrRef &S2 ) if( L > S2.length ) L = S2.length; for( int K {}; K < L; K++ ) { - const int d = utils::toupper( S1.chars[K] ) - utils::toupper( S2.chars[K] ); - if( d ) return d; + if( const int d = utils::toupper( S1.chars[K] ) - utils::toupper( S2.chars[K] ) ) + return d; } return S1.length - S2.length; } diff --git a/src/gdlib/utils.cpp b/src/gdlib/utils.cpp index 6b871fc..02b459a 100644 --- a/src/gdlib/utils.cpp +++ b/src/gdlib/utils.cpp @@ -213,7 +213,7 @@ bool hasCharLt( const std::string_view s, int n ) return anychar( [&n]( char c ) { return (int) c < n; }, s ); } -double round( double n, int ndigits ) +double round( const double n, const int ndigits ) { return std::round( n * std::pow( 10, ndigits ) ) * pow( 10, -ndigits ); } diff --git a/src/gdlib/utils.h b/src/gdlib/utils.h index ef86ee1..c0c1de1 100644 --- a/src/gdlib/utils.h +++ b/src/gdlib/utils.h @@ -178,7 +178,11 @@ class charset { chars.set(i); } - [[nodiscard]] bool contains(char c) const { + [[nodiscard]] bool contains(const char c) const { + return chars[c+offset]; + } + + [[nodiscard]] bool operator[](const char c) const { return chars[c+offset]; } @@ -492,6 +496,13 @@ bool hasCharLt( std::string_view s, int n ); double round( double n, int ndigits ); +// since std::round behaves differently from Delphi's System.Round +template +T round( const double n) +{ + return static_cast(n >= 0 ? n+0.5 : n-0.5); +} + void replaceChar( char a, char b, std::string &s ); std::vector substrPositions( std::string_view s, std::string_view substr ); @@ -727,7 +738,7 @@ inline char *NewString( const char *s, int64_t queryPeakRSS(); -inline int ord( const char c ) +inline auto ord( const char c ) { return static_cast( c ); } @@ -746,14 +757,32 @@ std::string IntToStrW( int n, int w, char blankChar = ' ' ); void trimLeft( std::string &s ); template -inline int indexOfSameText(const std::array &strs, const std::string &s) { +int indexOfSameText(const std::array &strs, const std::string &s) { int i{firstValid}; for(const std::string &s2 : strs) { if(sameText(s, s2)) return i; - i++; + ++i; } return firstValid-1; } +template +auto ui8(const T x) +{ + return static_cast( x ); +} + +template +auto ui16(const T x) +{ + return static_cast( x ); +} + +template +auto ui32(const T x) +{ + return static_cast( x ); +} + }// namespace utils diff --git a/src/global/delphitypes.h b/src/global/delphitypes.h index 9c05008..0f29400 100644 --- a/src/global/delphitypes.h +++ b/src/global/delphitypes.h @@ -25,148 +25,56 @@ #pragma once + #include "modhead.h" #include #include #include -// only supported by MSVC so far :( -//#include #include -#include #include #include #include #include #include - // Interface namespace global::delphitypes { -class ShortStringHeap -{ - char *buf; - -public: - ShortStringHeap(const ShortStringHeap &other) { - buf = new char[other.buf[0]+2]; - std::memcpy(buf, other.buf, sizeof(char)*buf[0]+2); - } - - explicit ShortStringHeap( const char *s ) - { - auto l { std::strlen( s ) }; - buf = new char[l + 2]; - buf[0] = static_cast( l ); - std::memcpy( &buf[1], s, l + 1 ); - } - - ~ShortStringHeap() - { - delete[] buf; - } - - [[nodiscard]] std::string string() const - { - return &buf[1]; - } - - [[nodiscard]] char *c_str() const - { - return &buf[1]; - } - - char *d_str() - { - return buf; - } - - [[nodiscard]] uint8_t size() const - { - return (uint8_t) buf[0]; - } - - [[nodiscard]] bool empty() const - { - return !(uint8_t) buf[0]; - } - - bool operator==( const std::string &s ) const - { - if( s.length() != size() ) return false; - for( int i {}; i < size(); i++ ) - if( s[i] != buf[i + 1] ) return false; - return true; - } - - ShortStringHeap &operator=( const std::string &s ) - { - buf[0] = static_cast( s.length() ); - std::memcpy( &buf[1], s.c_str(), s.length() + 1 ); - return *this; - } -}; +// According to Embarcadero docs +// http://docwiki.embarcadero.com/RADStudio/Sydney/en/Simple_Types_(Delphi) +using Byte = uint8_t; // unsigned char +using Word = uint16_t;// unsigned short +using LongWord = uint32_t; // unsigned +using Cardinal = uint32_t;// unsigned int +using Longint = int32_t; +using Int32 = int32_t;// int +using Int64 = int64_t;// long long +using Shortint = signed char; +using Integer = int; +using Smallint = int16_t;// short -class ShortString +// mimick Pascal "array[lbIncl..ubIncl] of T" +// internally uses std::array. lbIncl maps to 0, ubIncl maps to size()-1 +template +class OffsetArray : public std::array { - uint8_t length {}; - std::array buf {}; - + // Hide direct buffer access as this could be error-prone + T *data() { return nullptr; } public: - explicit ShortString( const char *s ) : length { (uint8_t) std::strlen( s ) } - { - assert( std::strlen( s ) <= 254 ); - std::memcpy( buf.data(), s, length + 1 ); - } - - explicit ShortString( const std::string &s ) : ShortString( s.c_str() ) - { - } - - [[nodiscard]] std::string string() const - { - return buf.data(); + T& operator[]( const int ix) { + assert( ix >= lbIncl && ix <= ubIncl && "Index must be in range!" ); + return std::array::operator[](ix - lbIncl); } - char *c_str() - { - return buf.data(); - } - - char *d_str() - { - return (char *) &length; - } - - [[nodiscard]] uint8_t size() const - { - return length; - } - - [[nodiscard]] bool empty() const - { - return !length; - } - - bool operator==( const std::string &s ) const - { - if( s.length() != length ) return false; - for( int i {}; i < length; i++ ) - if( s[i] != buf[i] ) return false; - return true; - } - - ShortString &operator=( const std::string &s ) - { - length = (uint8_t) s.length(); - std::memcpy( buf.data(), s.c_str(), length + 1 ); - return *this; + const T& operator[]( const int ix) const { + assert( ix >= lbIncl && ix <= ubIncl && "Index must be in range!" ); + return std::array::operator[](ix - lbIncl); } }; template -inline void FreeAndNil( T *&ptr ) +void FreeAndNil( T *&ptr ) { if( ptr ) { @@ -175,19 +83,6 @@ inline void FreeAndNil( T *&ptr ) } } -// According to Embarcadero docs -// http://docwiki.embarcadero.com/RADStudio/Sydney/en/Simple_Types_(Delphi) -using Byte = uint8_t; // unsigned char -using Word = uint16_t;// unsigned short -using LongWord = uint32_t; // unsigned -using Cardinal = uint32_t;// unsigned int -using Longint = int; -using Int32 = int32_t;// int -using Int64 = int64_t;// long long -using Shortint = signed char; -using Integer = int; -using Smallint = int16_t;// short - using tDateTime = double; using Text = std::fstream*; @@ -285,13 +180,19 @@ class Bounded return upperBoundIncl; } + // Number of elements from lower bound to upper bound (both inclusive) + constexpr static T getCount() + { + return upperBoundIncl - lowerBoundIncl + 1; + } + T *getStorage() { return &value; } }; -inline double frac( double v ) +inline double frac( const double v ) { return v - trunc( v ); } diff --git a/src/global/gmsspecs.cpp b/src/global/gmsspecs.cpp index 6e1a37f..51238fc 100644 --- a/src/global/gmsspecs.cpp +++ b/src/global/gmsspecs.cpp @@ -37,7 +37,7 @@ tgmsvalue mapval( double x ) if( x < valund ) return xvreal; if( x >= valacr ) return xvacr; x /= valund; - int k = static_cast( std::round( x ) ); + const int k = utils::round( x ); if( std::abs( k - x ) > 1.0e-5 ) return xvund; diff --git a/src/global/modhead.cpp b/src/global/modhead.cpp index b3fbd79..3636f71 100644 --- a/src/global/modhead.cpp +++ b/src/global/modhead.cpp @@ -31,8 +31,11 @@ using namespace std::literals::string_literals; namespace global::modhead { #if defined(__IN_CPPMEX__) -SWStream::SWStream() : std::ofstream { fn }, start { tellp() } +SWStream::SWStream() /*: std::ofstream { fn }, start { tellp() }*/ { + // somehow valgrind prefers it, when we do it here + open( fn ); + start = tellp(); } SWStream::~SWStream() diff --git a/src/global/modhead.h b/src/global/modhead.h index 1877c02..35b0f6e 100644 --- a/src/global/modhead.h +++ b/src/global/modhead.h @@ -54,7 +54,7 @@ namespace global::modhead #if defined( __IN_CPPMEX__ ) class SWStream final : public std::ofstream { - std::streampos start; + std::streampos start {}; const std::string fn { "stubwarnings.txt" }; public: SWStream(); diff --git a/src/gxfile.cpp b/src/gxfile.cpp index dfeaaa8..cef4a33 100644 --- a/src/gxfile.cpp +++ b/src/gxfile.cpp @@ -1296,25 +1296,29 @@ bool TGXFileObj::DoWrite( const int *AElements, const double *AVals ) } if( FDim == FCurrentDim && delta <= DeltaForWrite ) {// small change in last dimension - FFile->WriteByte( FCurrentDim + delta ); + assert(FCurrentDim >= 0 && FCurrentDim <= 255); + FFile->WriteByte( utils::ui8(FCurrentDim + delta) ); LastElem[FCurrentDim - 1] = AElements[FCurrentDim - 1]; } else {// general change - FFile->WriteByte( FDim ); + assert(FDim >= 0 && FDim <= 255); + FFile->WriteByte( static_cast(FDim) ); for( int D { FDim - 1 }; D < FCurrentDim; D++ ) { - int v { AElements[D] - MinElem[D] }; + const int v { AElements[D] - MinElem[D] }; switch( ElemType[D] ) { case TgdxElemSize::sz_integer: FFile->WriteInteger( v ); break; case TgdxElemSize::sz_word: - FFile->WriteWord( v ); + assert(v >= 0 && v <= 65535); + FFile->WriteWord( static_cast(v) ); break; case TgdxElemSize::sz_byte: - FFile->WriteByte( v ); + assert(v >= 0 && v <= 255); + FFile->WriteByte( static_cast(v) ); break; } LastElem[D] = AElements[D]; @@ -1325,10 +1329,10 @@ bool TGXFileObj::DoWrite( const int *AElements, const double *AVals ) { for( int DV {}; DV <= LastDataField; DV++ ) { - double X { AVals[DV] }; + const double X { AVals[DV] }; int64_t i64; - TDblClass dClass { dblInfo( X, i64 ) }; - int xv { vm_valund }; + const TDblClass dClass { dblInfo( X, i64 ) }; + uint8_t xv { vm_valund }; for( ; xv < vm_normal; xv++ ) if( i64 == intlValueMapI64[xv] ) break; if( xv == vm_normal ) @@ -1360,7 +1364,7 @@ bool TGXFileObj::DoWrite( const int *AElements, const double *AVals ) { FFile->WriteDouble( X ); if( X >= Zvalacr ) - AcronymList->CheckEntry( static_cast( std::round( X / Zvalacr ) ) ); + AcronymList->CheckEntry( utils::round( X / Zvalacr ) ); } } if( verboseTrace && TraceLevel >= TraceLevels::trl_all ) @@ -1454,9 +1458,9 @@ bool TGXFileObj::DoRead( double *AVals, int &AFDim ) if( MapSetText && AVals[GMS_VAL_LEVEL] != 0.0 && CurSyPtr->SDataType == dt_set ) {// remap settext number // NOTE: Not covered by unit tests yet. - double X { AVals[GMS_VAL_LEVEL] }; - int D { static_cast( std::round( X ) ) }; - if( std::abs( X - D ) < 1e-12 && D >= 0 && D <= SetTextList->GetCapacity() ) + const double X { AVals[GMS_VAL_LEVEL] }; + if( const int D { ( utils::round( X ) ) }; + std::abs( X - D ) < 1e-12 && D >= 0 && D <= SetTextList->GetCapacity() ) AVals[GMS_VAL_LEVEL] = MapSetText[D]; } if( verboseTrace && TraceLevel >= TraceLevels::trl_all ) @@ -1471,10 +1475,9 @@ double TGXFileObj::AcronymRemap( double V ) if(MapAcrToNaN) return intlValueMapDbl[GMS_SVIDX_NA]; - int orgIndx { static_cast( std::round( v / Zvalacr ) ) }, - N { AcronymList->FindEntry( orgIndx ) }, - newIndx {}; - if( N < 0 ) + const int orgIndx { ( utils::round( v / Zvalacr ) ) }; + int newIndx {}; + if( int N { AcronymList->FindEntry( orgIndx ) }; N < 0 ) {// not found // NOTE: Not covered by unit tests yet. if( NextAutoAcronym <= 0 ) newIndx = orgIndx; @@ -1505,14 +1508,11 @@ double TGXFileObj::AcronymRemap( double V ) if( V < Zvalacr ) return V;// NOTE: Not covered by unit tests yet. - else - { - if( V == 0.0 ) return 0.0; - if( std::isnan( V ) ) return intlValueMapDbl[vm_valna]; - if( std::isinf( V ) ) return V < 0.0 ? intlValueMapDbl[vm_valmin] : intlValueMapDbl[vm_valpin]; - if( std::isnormal( V ) ) return V < 0.0 ? V : GetAsAcronym( V ); - return intlValueMapDbl[vm_valna];// NOTE: Not covered by unit tests yet. - } + if( V == 0.0 ) return 0.0; + if( std::isnan( V ) ) return intlValueMapDbl[vm_valna]; + if( std::isinf( V ) ) return V < 0.0 ? intlValueMapDbl[vm_valmin] : intlValueMapDbl[vm_valpin]; + if( std::isnormal( V ) ) return V < 0.0 ? V : GetAsAcronym( V ); + return intlValueMapDbl[vm_valna];// NOTE: Not covered by unit tests yet. } void TGXFileObj::AddToErrorListDomErrs( const std::array &AElements, const double *AVals ) @@ -1526,8 +1526,7 @@ void TGXFileObj::AddToErrorListDomErrs( const std::arrayGetCount(); i++ ) @@ -1584,8 +1583,9 @@ bool TGXFileObj::ResultWillBeSorted( const int *ADomainNrs ) const break; default: if( UELTable->GetMapToUserStatus() >= TUELUserMapStatus::map_sorted ) continue; - const TDFilter *PFilter = FilterList->FindFilter( ADomainNrs[D] ); - if( !PFilter->FiltSorted ) return false; + if( const TDFilter *PFilter = FilterList->FindFilter( ADomainNrs[D] ); + !PFilter->FiltSorted ) + return false; break; } } @@ -3221,7 +3221,7 @@ int TGXFileObj::gdxAcronymAdd( const char *AName, const char *Txt, int AIndx ) int TGXFileObj::gdxAcronymIndex( double V ) const { - return V < Zvalacr ? 0 : static_cast( std::round( V / Zvalacr ) ); + return V < Zvalacr ? 0 : utils::round( V / Zvalacr ); } int TGXFileObj::gdxAcronymName( double V, char *AName ) diff --git a/src/rtl/idglobal_p3.cpp b/src/rtl/idglobal_p3.cpp index 02dee64..616aee8 100644 --- a/src/rtl/idglobal_p3.cpp +++ b/src/rtl/idglobal_p3.cpp @@ -38,12 +38,12 @@ // ============================================================================================================== namespace rtl::idglobal_p3 { -cardinal GetTickDiff( const cardinal AOldTickCount, const cardinal ANewTickCount ) +uint32_t GetTickDiff( const uint32_t AOldTickCount, const uint32_t ANewTickCount ) { return ANewTickCount >= AOldTickCount ? ANewTickCount - AOldTickCount : std::numeric_limits::max() - AOldTickCount + ANewTickCount; } -uint64_t GetTickCount() { +uint32_t GetTickCount() { #if defined( _WIN32 ) return ::GetTickCount(); #else diff --git a/src/rtl/idglobal_p3.h b/src/rtl/idglobal_p3.h index 00a9c58..ca37f35 100644 --- a/src/rtl/idglobal_p3.h +++ b/src/rtl/idglobal_p3.h @@ -31,7 +31,6 @@ // ============================================================================================================== namespace rtl::idglobal_p3 { -typedef uint64_t cardinal; -cardinal GetTickDiff( cardinal AOldTickCount, cardinal ANewTickCount ); -uint64_t GetTickCount(); +uint32_t GetTickDiff( uint32_t AOldTickCount, uint32_t ANewTickCount ); +uint32_t GetTickCount(); }// namespace rtl::idglobal_p3 diff --git a/src/rtl/p3io.cpp b/src/rtl/p3io.cpp index 3baa13c..b859230 100644 --- a/src/rtl/p3io.cpp +++ b/src/rtl/p3io.cpp @@ -251,85 +251,81 @@ void P3_Val_i(const char *s, int *i, int *code) * where d is a decimal digit unless preceded by the 0x or $, * in which case it is a hex digit */ -void P3_Val_i(const char *s, size_t slen, int *i, int *code) +void P3_Val_i( const char *s, size_t slen, int *i, int *code ) { std::array buffer; - char *end, *s2, *sd; - long int li; - int sign = 1; + std::memcpy( buffer.data(), s, sizeof( char ) * ( slen + 1 ) ); - std::memcpy(buffer.data(), s, sizeof(char)*(slen+1)); - - /* skip over blanks - * - Kylix 3 does not treat any other chars as whitespace - */ - for (s2 = (char *)buffer.data(); ' ' == *s2; s2++); - if ('+' == *s2) { - sd = s2+1; - } - else if ('-' == *s2) { + // skip over blanks + // - Kylix 3 does not treat any other chars as whitespace + char *s2, *sd; + for( s2 = buffer.data(); ' ' == *s2; s2++ ) {} + int sign { 1 }; + if( '+' == *s2 ) + sd = s2 + 1; + else if( '-' == *s2 ) + { sign = -1; - sd = s2+1; + sd = s2 + 1; } else sd = s2; - /* first check for the usual case - decimal digits */ - if (((*sd > '0') && (*sd <= '9')) || - (('0' == *sd) && ('\0' == sd[1] || (sd[1] >= '0' && sd[1] <= '9') )) ) { - li = strtol((char *)s2, (char **)&end, 10); - *i = li; - if ('\0' == *end) { /* reached the end, things went OK */ - *code = 0; - } - else - *code = (int)(end - (char *)buffer.data() + 1); + // first check for the usual case - decimal digits + if( (*sd > '0' && *sd <= '9') || ('0' == *sd && ( '\0' == sd[1] || (sd[1] >= '0' && sd[1] <= '9') )) ) + { + char *end; + const auto li = strtol( s2, &end, 10 ); + *i = static_cast(li); + // reached the end, things went OK + *code = '\0' == *end ? 0 : static_cast( end - buffer.data() + 1 ); return; } - /* if not a decimal string, - * must be either $ffff or 0xffff or an error */ - if ('$' == *sd) { - if ((sd[1] >= '0' && sd[1] <= '9') || (sd[1] >= 'A' && sd[1] <= 'F')) { // isxdigit - if (-1 == sign) + // if not a decimal string, + // must be either $ffff or 0xffff or an error + if( '$' == *sd ) + { + if( ( sd[1] >= '0' && sd[1] <= '9' ) || ( sd[1] >= 'A' && sd[1] <= 'F' ) ) + { + // isxdigit + if( -1 == sign ) *sd = '-'; else sd++; - li = strtol((char *)sd, (char **)&end, 16); - *i = li; - if ('\0' == *end) { /* reached the end, things went OK */ - *code = 0; - } - else - *code = (int)(end - (char *)buffer.data() + 1); + char *end; + const auto li = strtol( sd, &end, 16 ); + *i = static_cast(li); + // reached the end, things went OK + *code = '\0' == *end ? 0 : static_cast( end - buffer.data() + 1 ); } - else { + else + { *i = 0; sd++; - *code = (int)(sd - (char *)buffer.data() + 1); + *code = static_cast( sd - buffer.data() + 1 ); } return; } - else if (('0' == *sd) && - (('x' == sd[1]) || ('X' == sd[1])) - ) { - li = strtol((char *)s2, (char **)&end, 16); - *i = li; - if ('\0' == *end) { /* reached the end, things went OK */ + if( '0' == *sd && ( 'x' == sd[1] || 'X' == sd[1] ) ) + { + char *end; + const auto li = strtol( s2, &end, 16 ); + *i = static_cast(li); + // reached the end, things went OK + if( '\0' == *end ) *code = 0; - } - else { - /* we alread read the 0x, that is not an error for val */ - if (end < sd+2) - end = sd+2; - *code = (int)(end - (char *)buffer.data() + 1); + else + { + // we already read the 0x, that is not an error for val + if( end < sd + 2 ) + end = sd + 2; + *code = static_cast( end - buffer.data() + 1 ); } return; } - else { - *i = 0; - *code = (int)(sd - (char *)buffer.data() + 1); - } + *i = 0; + *code = static_cast( sd - buffer.data() + 1 ); } //================================================================================= diff --git a/src/rtl/p3process.cpp b/src/rtl/p3process.cpp index 9e1574c..25683f6 100644 --- a/src/rtl/p3process.cpp +++ b/src/rtl/p3process.cpp @@ -1215,7 +1215,7 @@ bool p3KillProcGroupTP( const TProcInfo &procInfo, TKillHow how ) #endif } -bool p3IsPIDValid( global::delphitypes::Cardinal pid ) +bool p3IsPIDValid( uint32_t pid ) { #ifdef _WIN32 DWORD p = pid; diff --git a/src/rtl/p3process.h b/src/rtl/p3process.h index 51b6253..6c96d96 100644 --- a/src/rtl/p3process.h +++ b/src/rtl/p3process.h @@ -62,7 +62,7 @@ int p3ASyncExecP( const std::string &cmdPtr, bool newConsole, TProcInfo &procInf int p3ASyncStatus( TProcInfo &procInfo, int &progRC, std::string &msg ); bool p3KillProcGroupTP( const TProcInfo &procInfo, TKillHow how ); -bool p3IsPIDValid( global::delphitypes::Cardinal pid ); +bool p3IsPIDValid( uint32_t pid ); using tCtrlHandler = void(*)(); diff --git a/src/rtl/p3utils.cpp b/src/rtl/p3utils.cpp index 590aa35..89c7c0f 100644 --- a/src/rtl/p3utils.cpp +++ b/src/rtl/p3utils.cpp @@ -81,6 +81,7 @@ using namespace rtl::sysutils_p3; using namespace rtl::p3platform; using namespace std::literals::string_literals; +using utils::ui32; // ============================================================================================================== // Implementation @@ -162,7 +163,7 @@ uint32_t P3GetEnvPC( const std::string &name, char *buf, uint32_t bufSize ) #else const char *p = getenv( name.c_str() ); if( !p ) return 0;// no match in the env - const auto psiz = strlen( p ) + 1; + const auto psiz = utils::ui32(strlen( p ) + 1); if( psiz <= bufSize ) { // it fits: copy it over @@ -491,13 +492,12 @@ int p3FileRead( Tp3FileHandle h, char *buffer, uint32_t buflen, uint32_t &numRea res = EIO; } #else - auto rc = read( h, buffer, buflen ); - if( rc < 0 ) + if( const auto rc = read( h, buffer, buflen ); rc < 0 ) { res = errno; numRead = 0; } - else numRead = rc; + else numRead = ui32(rc); #endif return res; } @@ -514,13 +514,12 @@ int p3FileWrite( Tp3FileHandle h, const char *buffer, uint32_t buflen, uint32_t res = EIO; } #else - auto rc = write(h, buffer, buflen); - if (rc < 0) { + if ( const auto rc = write( h, buffer, buflen ); rc < 0) { res = errno; numWritten = 0; } else - numWritten = rc; + numWritten = ui32(rc); #endif return res; } @@ -1107,7 +1106,7 @@ int xGetExecName( std::string &execName, std::string &msg ) } else { - ssz = std::min( execBuf.size() - 1, ssz ); + ssz = std::min( execBuf.size() - 1, ssz ); rc = 0; } #elif defined( _WIN32 ) diff --git a/src/rtl/sysutils_p3.cpp b/src/rtl/sysutils_p3.cpp index 72784c3..9053445 100644 --- a/src/rtl/sysutils_p3.cpp +++ b/src/rtl/sysutils_p3.cpp @@ -27,9 +27,12 @@ #include // for strerror, size_t, strcmp, strcpy #include "sysutils_p3.h" -#include "p3platform.h" // for OSFileType, tOSFileType +#include "p3platform.h"// for OSFileType, tOSFileType + #include "global/unit.h" // for UNIT_INIT_FINI +#include "gdlib/utils.h" // for ui16 + #if defined( _WIN32 ) #include #include @@ -40,10 +43,12 @@ #include #endif -using namespace global::delphitypes; + using namespace rtl::p3platform; using namespace std::literals::string_literals; +using utils::ui16; + // ============================================================================================================== // Implementation // ============================================================================================================== @@ -93,14 +98,14 @@ bool FileExists( const std::string &FileName ) #endif } -static TTimeStamp DateTimeToTimeStamp( tDateTime DateTime ) +static TTimeStamp DateTimeToTimeStamp( global::delphitypes::tDateTime DateTime ) { return { - static_cast( round( std::abs( frac( DateTime ) ) * MSecsPerDay ) ), + static_cast( round( std::abs( global::delphitypes::frac( DateTime ) ) * MSecsPerDay ) ), static_cast( trunc( DateTime ) + DateDelta ) }; } -void DecodeTime( const tDateTime DateTime, uint16_t &Hour, uint16_t &Min, uint16_t &Sec, uint16_t &Msec ) +void DecodeTime( const global::delphitypes::tDateTime DateTime, uint16_t &Hour, uint16_t &Min, uint16_t &Sec, uint16_t &Msec ) { uint16_t MinCount, MSecCount; const auto tmp = DateTimeToTimeStamp( DateTime ); @@ -112,8 +117,8 @@ void DecodeTime( const tDateTime DateTime, uint16_t &Hour, uint16_t &Min, uint16 void DivMod( const int Dividend, const uint16_t Divisor, uint16_t &Result, uint16_t &Remainder ) { const auto res = div( Dividend, Divisor ); - Result = res.quot; - Remainder = res.rem; + Result = static_cast(res.quot); + Remainder = static_cast(res.rem); } double EncodeDate( uint16_t Year, uint16_t Month, const uint16_t Day ) @@ -286,7 +291,7 @@ std::string IncludeTrailingPathDelimiter( const std::string &S ) return S + PathDelim; } -const std::array +const std::array daysPerMonthRegularYear = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, daysPerMonthLeapYear = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; @@ -490,7 +495,7 @@ void FindClose( TSearchRec &F ) bool tryEncodeDate( const uint16_t year, const uint16_t month, uint16_t day, double &date ) { - if( const std::array &daysPerMonth = isLeapYear( year ) ? daysPerMonthLeapYear : daysPerMonthRegularYear; + if( const std::array &daysPerMonth = isLeapYear( year ) ? daysPerMonthLeapYear : daysPerMonthRegularYear; year >= 1 && year <= 9999 && month >= 1 && month <= 12 && day >= 1 && day <= daysPerMonth[month - 1] ) { const int stop = month - 1; @@ -535,8 +540,8 @@ double Now() if(gettimeofday(&tv, nullptr) || !localtime_r(&tv.tv_sec, <)) return 0.0; double dnow, tnow; - const bool rc1 = tryEncodeDate( lt.tm_year + 1900, lt.tm_mon + 1, lt.tm_mday, dnow ); - const bool rc2 = tryEncodeTime (lt.tm_hour, lt.tm_min, lt.tm_sec, tv.tv_usec/1000, tnow); + const bool rc1 = tryEncodeDate( ui16(lt.tm_year + 1900), ui16(lt.tm_mon + 1), ui16(lt.tm_mday), dnow ); + const bool rc2 = tryEncodeTime (ui16(lt.tm_hour), ui16(lt.tm_min), ui16(lt.tm_sec), ui16(tv.tv_usec/1000), tnow); return rc1 && rc2 ? dnow + tnow : 0.0; #endif } @@ -560,7 +565,7 @@ static bool DecodeDateFully(const double DateTime, uint16_t &Year, uint16_t &Mon Year = Month = Day = DOW = 0; return false; } - DOW = T % 7 + 1; + DOW = ui16(T % 7 + 1); T--; uint16_t Y = 1; while( T >= D400 ) @@ -575,9 +580,9 @@ static bool DecodeDateFully(const double DateTime, uint16_t &Year, uint16_t &Mon I--; D += D100; } - Y += I * 100; + Y += ui16(I * 100); DivMod( D, D4, I, D ); - Y += I * 4; + Y += ui16(I * 4); DivMod( D, D1, I, D ); if( I == 4 ) { @@ -600,7 +605,7 @@ static bool DecodeDateFully(const double DateTime, uint16_t &Year, uint16_t &Mon return res; } -void DecodeDate( const tDateTime DateTime, uint16_t &Year, uint16_t &Month, uint16_t &Day ) +void DecodeDate( const global::delphitypes::tDateTime DateTime, uint16_t &Year, uint16_t &Month, uint16_t &Day ) { uint16_t Dummy {}; DecodeDateFully(DateTime, Year, Month, Day, Dummy); @@ -700,8 +705,8 @@ double FileDateToDateTime( int fd ) time_t tim; tim = fd; localtime_r( &tim, &ut ); - return EncodeDate( ut.tm_year + 1900, ut.tm_mon + 1, ut.tm_mday ) + - EncodeTime( ut.tm_hour, ut.tm_min, ut.tm_sec, 0 ); + return EncodeDate( ui16(ut.tm_year + 1900), ui16(ut.tm_mon + 1), ui16(ut.tm_mday) ) + + EncodeTime( ui16(ut.tm_hour), ui16(ut.tm_min), ui16(ut.tm_sec), 0 ); #endif } @@ -714,8 +719,8 @@ int DateTimeToFileDate( double dt ) DecodeTime( dt, hour, min, sec, msec ); #if defined(_WIN32) LongRec lr { - ( sec >> 1 ) | ( min << 5 ) | ( hour << 11 ), - day | ( month << 5 ) | ( (year - 1980) << 9 ) + static_cast( ( sec >> 1 ) | ( min << 5 ) | ( hour << 11 ) ), + static_cast(day | ( month << 5 ) | ( (year - 1980) << 9 )) }; static_assert( sizeof( LongRec ) == sizeof( int ) ); int res; diff --git a/src/tests/gdlib/glookuptests.cpp b/src/tests/gdlib/glookuptests.cpp index 386be0a..eaa1cf0 100644 --- a/src/tests/gdlib/glookuptests.cpp +++ b/src/tests/gdlib/glookuptests.cpp @@ -57,12 +57,13 @@ struct Item { } }; -class MyMapping : public TGAMSRecList +class MyMapping final : public TGAMSRecList { - std::pair AccessRecord( Item *prec ) override + std::pair AccessRecord( Item *prec ) override { - gdlib::strutilx::DelphiStrRef dsr {}; - dsr.length = static_cast(prec->s.length()); + DelphiStrRef dsr {}; + assert(prec->s.length() <= 255); + dsr.length = static_cast(prec->s.length()); dsr.chars = const_cast( prec->s.c_str() ); return { dsr, &prec->nextBucketIx }; } diff --git a/src/tests/gdlib/gmsdatatests.cpp b/src/tests/gdlib/gmsdatatests.cpp index 77eeebe..41eb9f2 100644 --- a/src/tests/gdlib/gmsdatatests.cpp +++ b/src/tests/gdlib/gmsdatatests.cpp @@ -93,6 +93,48 @@ TEST_CASE( "Test basic usage of TTblGamsData" ) } } +TEST_CASE( "Test sorting more elaborately" ) +{ + std::array keys {}; + std::array vals {}; + TTblGamsData gdl { 3, sizeof( double ) * 2 }; + + // A more elaborate test of the sorting + gdl.Clear(); + std::fill( vals.begin(), vals.end(), 0 ); + vals.front() = 23.0; + + using triple = std::array; + constexpr std::array unsortedKeys { + triple {1, 2, 3}, + triple {1, 1, 3}, + triple {2, 0, 0}, + triple {0, 9, 9} + }; + + constexpr std::array sortedKeys { + triple { 0, 9, 9 }, + triple { 1, 1, 3 }, + triple { 1, 2, 3 }, + triple { 2, 0, 0 } + }; + + for (const auto& kt : unsortedKeys) { + std::copy_n( kt.begin(), 3, keys.begin() ); + std::fill( keys.begin() + 3, keys.end(), 0 ); + gdl.AddRecord( keys.data(), vals.data() ); + } + + gdl.Sort(); + + for (int i{}; i < gdl.GetCount(); i++) { + gdl.GetRecord( i, keys.data(), vals.data() ); + for( int j {}; j<3; j++ ) + REQUIRE_EQ( sortedKeys[i][j], keys[j] ); + REQUIRE_EQ( 23.0, vals.front() ); + } +} + TEST_SUITE_END(); } diff --git a/src/tests/gdlib/utilstests.cpp b/src/tests/gdlib/utilstests.cpp index 8fb9cae..588323c 100644 --- a/src/tests/gdlib/utilstests.cpp +++ b/src/tests/gdlib/utilstests.cpp @@ -426,6 +426,17 @@ TEST_CASE( "Test rounding to n-digits" ) REQUIRE_LT( 23.0 - utils::round( 23.4242, 0 ), eps ); } +TEST_CASE( "Test double to integral type rounding function that should mimic System.Round Delphi behavior" ) +{ + REQUIRE_EQ(0, utils::round(0.45)); + REQUIRE_EQ(0, utils::round(-0.45)); + // round tie away from 0 + REQUIRE_EQ(-1, utils::round(-0.5)); + REQUIRE_EQ(1, utils::round(0.5)); + REQUIRE_EQ(-3, utils::round(-2.5)); + REQUIRE_EQ(3, utils::round(2.5)); +} + TEST_CASE( "Test replacing specific char with another one in a string (in place)" ) { std::string s { "Letter X"s }; diff --git a/src/tests/gdxtests.cpp b/src/tests/gdxtests.cpp index 917b845..70476b1 100644 --- a/src/tests/gdxtests.cpp +++ b/src/tests/gdxtests.cpp @@ -48,11 +48,14 @@ #include // for tuple #include // for pair #include +#include #include "gdxtests.h" +#include "gdlib/strindexbuf.h" using namespace std::literals::string_literals; using namespace gdx; +using namespace gdlib::strindexbuf; namespace gdx::tests::gdxtests { @@ -664,7 +667,7 @@ TEST_CASE( "Test write and read record in string mode" ) REQUIRE( pgx.gdxDataWriteStrStart( "mysym", stillOk.c_str(), 1, dt_par, 0 ) ); values[GMS_VAL_LEVEL] = 3.141; - char empty = '\0'; + constexpr char empty = '\0'; const char *emptyPtr = ∅ REQUIRE( pgx.gdxDataWriteStr( &emptyPtr, values.data() ) ); @@ -2010,20 +2013,20 @@ TEST_CASE("Test filter example from README") REQUIRE( gdx.gdxFilterRegister(1000) ); REQUIRE( gdx.gdxFilterRegister(2000) ); REQUIRE( gdx.gdxFilterRegisterDone() ); - std::array filterAction {123, gdx::DOMC_EXPAND}; + const std::array filterAction {123, gdx::DOMC_EXPAND}; int NrUnMapped, LastMapped; REQUIRE( gdx.gdxUMUelInfo( NrUnMapped, LastMapped ) ); int SyNr; REQUIRE( gdx.gdxFindSymbol( "A", SyNr ) ); - std::array SyName; + std::array SyName {}; int SyDim, SyTyp; REQUIRE( gdx.gdxSymbolInfo( SyNr, SyName.data(), SyDim, SyTyp ) ); REQUIRE_EQ( SyDim, 2 ); REQUIRE_EQ( SyTyp, dt_par ); int NrRecs; REQUIRE( gdx.gdxDataReadFilteredStart( SyNr, filterAction.data(), NrRecs ) ); - std::array IndxI; - std::array Values; + std::array IndxI {}; + std::array Values {}; int DimFrst; for( int N { 1 }; N <= NrRecs; N++ ) { @@ -2297,7 +2300,7 @@ TEST_CASE( "Test reading reading GDX files in legacy versions (V5 and V6)" ) { const auto outDir { "out"s + versSuff }, cmd { gamsToolCall( "gdxcopy"s ) + " -"s + versSuff + " "s + modelName + ".gdx "s + outDir }; - int rc { std::system( cmd.c_str() ) }; + const int rc { std::system( cmd.c_str() ) }; if( !rc ) { const auto convertedGDXfn { modelName + "_"s + versSuff + ".gdx"s }; @@ -2320,10 +2323,10 @@ TEST_CASE( "Test reading reading GDX files in legacy versions (V5 and V6)" ) TEST_CASE( "Re-create basic dc.gms (from idc01) test" ) { - auto fn { "twosets.gdx"s }; + const auto fn { "twosets.gdx"s }; testWrite( fn, []( TGXFileObj &pgx ) { StrIndexBuffers sib, sib2; - TgdxValues values {}; + const TgdxValues values {}; REQUIRE( pgx.gdxDataWriteStrStart( "k", "", 1, dt_set, 0 ) ); for( int i {}; i < 6; i++ ) diff --git a/src/tests/gdxtests.h b/src/tests/gdxtests.h index 9469548..29980e1 100644 --- a/src/tests/gdxtests.h +++ b/src/tests/gdxtests.h @@ -28,8 +28,6 @@ #include // for function #include // for string, operator""s #include // for vector -#include // for array, array<>::value_type -#include // for assert namespace gdx::tests::gdxtests { @@ -48,85 +46,4 @@ void commonSetGetDomainTests( const std::vector &domainNames, const void testReadModelGDX( const std::string &model, const std::function &func ); void testWithCompressConvert( bool compress, const std::string &convert ); -class StrRef -{ - char *s; - -public: - explicit StrRef( char *_s ) : s( _s ) {} - - StrRef &operator=( const std::string &other ) - { - std::memcpy( s, other.c_str(), sizeof( char ) * ( other.length() + 1 ) ); - return *this; - } - - const char *c_str() - { - return s; - } - - [[nodiscard]] bool empty() const - { - return s[0] == '\0'; - } - - explicit operator std::string() const - { - std::string res; - res.assign( s ); - return res; - } - - [[nodiscard]] std::string str() const - { - std::string res; - res.assign( s ); - return res; - } - - bool operator==( const std::string &other ) - { - return !std::strcmp( other.c_str(), s ); - } -}; - -using TgdxStrIndex = std::array; - -class StrIndexBuffers -{ - std::array, GMS_MAX_INDEX_DIM> bufContents {}; - std::array bufPtrs {}; - -public: - explicit StrIndexBuffers( const TgdxStrIndex *strIndex = nullptr ) - { - for( int i {}; i < (int) bufPtrs.size(); i++ ) - { - bufPtrs[i] = bufContents[i].data(); - if( strIndex ) - std::memcpy( bufPtrs[i], ( *strIndex )[i].c_str(), ( *strIndex )[i].length() + 1 ); - } - } - - StrRef operator[]( int index ) - { - return StrRef { bufPtrs[index] }; - } - - char **ptrs() { return bufPtrs.data(); } - const char **cptrs() { return (const char **) bufPtrs.data(); } - - void clear() - { - for( int i {}; i < (int) bufContents.size(); i++ ) - bufContents[i].fill( 0 ); - } - - StrRef front() - { - return StrRef { bufPtrs[0] }; - } -}; - }// namespace gdx::tests::gdxtests \ No newline at end of file diff --git a/src/yaml2doxy.py b/src/yaml2doxy.py index 1ba1003..b4a5f6c 100644 --- a/src/yaml2doxy.py +++ b/src/yaml2doxy.py @@ -74,7 +74,7 @@ def map_type_gen(func_ptrs, for_cpp=True): cSVA='const double *', ptr='void *', D='double', - int64='int64_t' if for_cpp else 'int') + int64='int64_t' if for_cpp else 'INT64') func_ptr_types = { func_name: func_attrs['function'] + '_t'