diff --git a/.github/workflows/ctest.yml b/.github/workflows/ctest.yml new file mode 100644 index 00000000..191cefb2 --- /dev/null +++ b/.github/workflows/ctest.yml @@ -0,0 +1,138 @@ +# This starter workflow is for a CMake project running on multiple platforms. There is a different starter workflow if you just want a single platform. +# See: https://github.com/actions/starter-workflows/blob/main/ci/cmake-single-platform.yml +name: Test bit7z on multiple configurations +on: [pull_request, push] + +jobs: + build: + runs-on: ${{ matrix.os }} + if: | + github.event_name == 'pull_request' + || contains(github.event.head_commit.message, '[test]') + || startsWith(github.ref, 'refs/tags/v') + + strategy: + # Set fail-fast to false to ensure that feedback is delivered for all matrix combinations. Consider changing this to true when your workflow is stable. + fail-fast: false + + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] + build_type: [Debug, Release] + c_compiler: [gcc, clang, cl] + bit7z_auto_format: [OFF, ON] + bit7z_regex_matching: [OFF, ON] + bit7z_link_libcpp: [OFF, ON] + bit7z_use_native_string: [OFF, ON] + bit7z_auto_prefix_long_paths: [OFF, ON] + bit7z_use_system_codepage: [OFF, ON] + bit7z_path_sanitization: [OFF, ON] + include: + - os: windows-latest + c_compiler: cl + cpp_compiler: cl + use_system_7zip: ON + - os: ubuntu-latest + c_compiler: gcc + cpp_compiler: g++ + use_system_7zip: OFF + - os: ubuntu-latest + c_compiler: clang + cpp_compiler: clang++ + use_system_7zip: OFF + - os: macos-latest + c_compiler: clang + cpp_compiler: clang++ + use_system_7zip: OFF + exclude: + - os: windows-latest + c_compiler: gcc + - os: windows-latest + c_compiler: clang + - os: windows-latest + bit7z_link_libcpp: ON + - os: ubuntu-latest + c_compiler: cl + - os: ubuntu-latest + c_compiler: gcc + bit7z_link_libcpp: ON + - os: ubuntu-latest + bit7z_use_native_string: ON + - os: ubuntu-latest + bit7z_auto_prefix_long_paths: ON + - os: ubuntu-latest + bit7z_use_system_codepage: ON + - os: ubuntu-latest + bit7z_path_sanitization: ON + - os: macos-latest + c_compiler: cl + - os: macos-latest + c_compiler: gcc + - os: macos-latest + bit7z_use_native_string: ON + - os: macos-latest + bit7z_auto_prefix_long_paths: ON + - os: macos-latest + bit7z_use_system_codepage: ON + - os: macos-latest + bit7z_path_sanitization: ON + + steps: + - uses: actions/checkout@v3 + + - name: Set reusable strings + # Turn repeated input strings (such as the build output directory) into step outputs. These step outputs can be used throughout the workflow file. + id: strings + shell: bash + run: | + echo "build-output-dir=${{ github.workspace }}/build" >> "$GITHUB_OUTPUT" + + - name: Configure CMake + # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. + # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type + run: > + cmake -B ${{ steps.strings.outputs.build-output-dir }} + -DCMAKE_CXX_COMPILER=${{ matrix.cpp_compiler }} + -DCMAKE_C_COMPILER=${{ matrix.c_compiler }} + -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} + -DBIT7Z_BUILD_TESTS=ON + -DBIT7Z_ENABLE_SANITIZERS=ON + -DBIT7Z_TESTS_USE_SYSTEM_7ZIP=${{ matrix.use_system_7zip }} + -DBIT7Z_AUTO_FORMAT=${{ matrix.bit7z_auto_format }} + -DBIT7Z_REGEX_MATCHING=${{ matrix.bit7z_regex_matching }} + -DBIT7Z_USE_NATIVE_STRING=${{ matrix.bit7z_use_native_string }} + -DBIT7Z_LINK_LIBCPP=${{ matrix.bit7z_link_libcpp }} + -DBIT7Z_AUTO_PREFIX_LONG_PATHS=${{ matrix.bit7z_auto_prefix_long_paths }} + -DBIT7Z_USE_SYSTEM_CODEPAGE=${{ matrix.bit7z_use_system_codepage }} + -DBIT7Z_PATH_SANITIZATION=${{ matrix.bit7z_path_sanitization }} + -S ${{ github.workspace }} + + - name: Build bit7z + # Build your program with the given configuration. Note that --config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + run: cmake --build ${{ steps.strings.outputs.build-output-dir }} --config ${{ matrix.build_type }} --parallel + + - name: Build 7z.so for tests (Ubuntu) + shell: bash + if: matrix.os == 'ubuntu-latest' + run: | + git clone --depth 1 https://github.com/rikyoz/7-Zip ${{ github.workspace }}/../7-zip + cd ${{ github.workspace }}/../7-zip/CPP/7zip/Bundles/Format7zF/ + make -j -f ../../cmpl_gcc.mak + cp b/g/7z.so ${{ github.workspace }}/bin/x64/7z.so + + - name: Build 7z.so for tests (macOS) + shell: bash + if: matrix.os == 'macos-latest' + run: | + git clone --depth 1 https://github.com/rikyoz/7-Zip ${{ github.workspace }}/../7-zip + cd ${{ github.workspace }}/../7-zip/CPP/7zip/Bundles/Format7zF/ + make -j -f ../../cmpl_mac_x64.mak + cp b/m_x64/7z.so ${{ github.workspace }}/bin/x64/7z.so + + - name: Test bit7z + working-directory: ${{ steps.strings.outputs.build-output-dir }} + # Disabling alloc_dealloc_mismatch for address sanitizer until https://github.com/llvm/llvm-project/issues/52771 is fixed + env: + ASAN_OPTIONS: alloc_dealloc_mismatch=0 + # Execute tests defined by the CMake configuration. Note that --build-config is needed because the default Windows generator is a multi-config generator (Visual Studio generator). + # See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail + run: ctest --build-target bit7z-tests --build-config ${{ matrix.build_type }} --output-on-failure diff --git a/CMakeLists.txt b/CMakeLists.txt index 23749e59..6ef13148 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required( VERSION 3.11 ) project( bit7z - VERSION 4.0.3 + VERSION 4.0.4 DESCRIPTION "A C++ static library offering a clean and simple interface to the 7-zip/p7zip shared libraries" HOMEPAGE_URL "https://github.com/rikyoz/bit7z/" ) set( CMAKE_VERBOSE_MAKEFILE ON CACHE BOOL "ON" ) @@ -95,6 +95,7 @@ set( HEADERS src/internal/stdinputitem.hpp src/internal/streamextractcallback.hpp src/internal/streamutil.hpp + src/internal/stringutil.hpp src/internal/updatecallback.hpp src/internal/util.hpp src/internal/windows.hpp ) @@ -156,8 +157,8 @@ set( SOURCES src/internal/renameditem.cpp src/internal/stdinputitem.cpp src/internal/streamextractcallback.cpp + src/internal/stringutil.cpp src/internal/updatecallback.cpp - src/internal/util.cpp src/internal/windows.cpp ) # library output file name options @@ -228,6 +229,9 @@ if( UNIX ) target_link_libraries( ${LIB_TARGET} PUBLIC ${CMAKE_DL_LIBS} ) endif() +# sanitizers +include( cmake/Sanitizers.cmake ) + # tests if( BIT7Z_BUILD_TESTS ) enable_testing() diff --git a/cmake/CompilerOptions.cmake b/cmake/CompilerOptions.cmake index a0861a59..107c26c1 100644 --- a/cmake/CompilerOptions.cmake +++ b/cmake/CompilerOptions.cmake @@ -68,7 +68,7 @@ if( MSVC ) endforeach() endif() else() - target_compile_options( ${LIB_TARGET} PRIVATE -Wall -Wextra -Werror ) + target_compile_options( ${LIB_TARGET} PRIVATE -Wall -Wextra -Werror -Wconversion -Wsign-conversion ) endif() # Extra warning flags for Clang @@ -122,6 +122,6 @@ if( CMAKE_CXX_COMPILER_ID MATCHES "GNU" ) endif() if( CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 7.0 ) # Extra warning flags for GCC 7.0+ - target_compile_options( ${LIB_TARGET} PRIVATE -Wduplicated-branches ) + target_compile_options( ${LIB_TARGET} PRIVATE -Wduplicated-branches -Wrestrict ) endif() endif() \ No newline at end of file diff --git a/cmake/Dependencies.cmake b/cmake/Dependencies.cmake index dfa3ddb6..7fd8b764 100644 --- a/cmake/Dependencies.cmake +++ b/cmake/Dependencies.cmake @@ -1,11 +1,14 @@ # Downloading the CPM.cmake package manager -set( CPM_DOWNLOAD_VERSION 0.38.5 ) +set( CPM_DOWNLOAD_VERSION 0.38.6 ) +set( CPM_DOWNLOAD_HASH 11c3fa5f1ba14f15d31c2fb63dbc8628ee133d81c8d764caad9a8db9e0bacb07 ) set( CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake" ) -if( NOT ( EXISTS ${CPM_DOWNLOAD_LOCATION} )) +if( NOT ( EXISTS ${CPM_DOWNLOAD_LOCATION} ) ) message( STATUS "Downloading CPM.cmake to ${CPM_DOWNLOAD_LOCATION}" ) file( DOWNLOAD https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake ${CPM_DOWNLOAD_LOCATION} + SHOW_PROGRESS + EXPECTED_HASH SHA256=${CPM_DOWNLOAD_HASH} ) endif() include( ${CPM_DOWNLOAD_LOCATION} ) diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake new file mode 100644 index 00000000..7e3c1feb --- /dev/null +++ b/cmake/Sanitizers.cmake @@ -0,0 +1,54 @@ +macro( add_sanitizer name ) + set( CMAKE_REQUIRED_FLAGS "-fsanitize=${name}" ) + check_cxx_compiler_flag( -fsanitize=${name} COMPILER_SUPPORTS_SANITIZE_${name} ) + if( COMPILER_SUPPORTS_SANITIZE_${name} ) + message( STATUS "Supported sanitizer: ${name}" ) + set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=${name}" ) + else() + message( STATUS "Unsupported sanitizer: ${name}" ) + endif() + unset( CMAKE_REQUIRED_FLAGS ) +endmacro() + +option( BIT7Z_ENABLE_SANITIZERS "Enable or disable compiling with sanitizers" ) +message( STATUS "Enable sanitizers: ${BIT7Z_ENABLE_SANITIZERS}" ) +if ( BIT7Z_ENABLE_SANITIZERS ) + if( MSVC ) + set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /RTCsu /analyze /guard:cf" ) + elseif( NOT WIN32 ) # GCC/Clang on Linux/macOS (i.e., not MinGW) + set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer" ) + + if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "6.0.0" ) + set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -static-libsan" ) + endif() + + # Use the highest stack protection level supported by the target toolchain. + include( CheckCXXCompilerFlag ) + check_cxx_compiler_flag( -fstack-protector-strong COMPILER_SUPPORT_STRONG_STACK_PROTECTOR ) + check_cxx_compiler_flag( -fstack-protector COMPILER_SUPPORT_STACK_PROTECTOR ) + if( COMPILER_SUPPORT_STRONG_STACK_PROTECTOR ) + set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector-strong" ) + elseif( COMPILER_SUPPORT_STACK_PROTECTOR ) + set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector" ) + endif() + + # For some reasons, the address sanitizer gives a CHECK failed error on versions of Clang before the 3.9 + # Also, on old versions of GCC the sanitizer give some false positives. + if( ( NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "5.0" ) OR + ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "3.9.0" ) ) + add_sanitizer( address ) + check_cxx_compiler_flag( -fsanitize-address-use-after-scope COMPILER_SUPPORT_SANITIZE_USE_AFTER_SCOPE ) + if( COMPILER_SUPPORT_SANITIZE_USE_AFTER_SCOPE ) + set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize-address-use-after-scope" ) + endif() + endif() + + add_sanitizer( float-divide-by-zero ) + add_sanitizer( implicit-conversion ) + add_sanitizer( integer ) + add_sanitizer( leak ) + add_sanitizer( local-bounds ) + add_sanitizer( nullability ) + add_sanitizer( undefined ) + endif() +endif() \ No newline at end of file diff --git a/src/bit7zlibrary.cpp b/src/bit7zlibrary.cpp index 6be63801..0e14757c 100644 --- a/src/bit7zlibrary.cpp +++ b/src/bit7zlibrary.cpp @@ -13,8 +13,9 @@ #include "bit7zlibrary.hpp" #include "bitexception.hpp" #include "bitformat.hpp" +#include "internal/com.hpp" #include "internal/guids.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" #include <7zip/Archive/IArchive.h> diff --git a/src/bitabstractarchivecreator.cpp b/src/bitabstractarchivecreator.cpp index 600e2da4..19f31c56 100644 --- a/src/bitabstractarchivecreator.cpp +++ b/src/bitabstractarchivecreator.cpp @@ -106,7 +106,7 @@ auto method_name( BitCompressionMethod method ) noexcept -> const wchar_t* { case BitCompressionMethod::Deflate64: return L"Deflate64"; default: - return L"Unknown"; //this should not happen! + return L"Unknown"; // This should not happen. } } @@ -185,7 +185,7 @@ void BitAbstractArchiveCreator::setPassword( const tstring& password ) { auto is_ascii( const tstring& str ) -> bool { return std::all_of( str.begin(), str.end(), []( tchar character ) -> bool { // Note: 7-zip supports the DEL character (code 127), while bit7z doesn't. - constexpr auto kFirstAsciiChar = 32; // A + constexpr auto kFirstAsciiChar = 32; // Space character constexpr auto kLastAsciiChar = 127; return character >= kFirstAsciiChar && character < kLastAsciiChar; } ); @@ -199,7 +199,7 @@ void BitAbstractArchiveCreator::setPassword( const tstring& password, bool crypt } #endif BitAbstractArchiveHandler::setPassword( password ); - mCryptHeaders = ( password.length() > 0 ) && cryptHeaders; + mCryptHeaders = !password.empty() && cryptHeaders; } void BitAbstractArchiveCreator::setCompressionLevel( BitCompressionLevel level ) noexcept { @@ -216,7 +216,7 @@ void BitAbstractArchiveCreator::setCompressionMethod( BitCompressionMethod metho if ( mFormat.hasFeature( FormatFeatures::MultipleMethods ) ) { /* even though the compression method is valid, we set it only if the format supports * different methods than the default one (i.e., setting BitCompressionMethod::BZip2 - * of a BitFormat::BZip2 archive does nothing!) */ + * of a BitFormat::BZip2 archive does nothing) */ mCompressionMethod = method; mDictionarySize = 0; //reset dictionary size to default value for the method mWordSize = 0; //reset word size to default value for the method diff --git a/src/bitarchiveeditor.cpp b/src/bitarchiveeditor.cpp index 69f1a49d..92880848 100644 --- a/src/bitarchiveeditor.cpp +++ b/src/bitarchiveeditor.cpp @@ -18,7 +18,7 @@ #include "internal/fsitem.hpp" #include "internal/renameditem.hpp" #include "internal/stdinputitem.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" namespace bit7z { @@ -51,43 +51,43 @@ BitArchiveEditor::~BitArchiveEditor() = default; void BitArchiveEditor::renameItem( uint32_t index, const tstring& newPath ) { checkIndex( index ); - mEditedItems[ index ] = std::make_unique< RenamedItem >( *inputArchive(), index, newPath ); + mEditedItems[ index ] = std::make_unique< RenamedItem >( *inputArchive(), index, newPath ); //-V108 } void BitArchiveEditor::renameItem( const tstring& oldPath, const tstring& newPath ) { auto index = findItem( oldPath ); - mEditedItems[ index ] = std::make_unique< RenamedItem >( *inputArchive(), index, newPath ); + mEditedItems[ index ] = std::make_unique< RenamedItem >( *inputArchive(), index, newPath ); //-V108 } void BitArchiveEditor::updateItem( uint32_t index, const tstring& inFile ) { checkIndex( index ); auto itemName = inputArchive()->itemProperty( index, BitProperty::Path ); - mEditedItems[ index ] = std::make_unique< FilesystemItem >( tstring_to_path( inFile ), itemName.getNativeString() ); + mEditedItems[ index ] = std::make_unique< FilesystemItem >( tstring_to_path( inFile ), itemName.getNativeString() ); //-V108 } void BitArchiveEditor::updateItem( uint32_t index, const std::vector< byte_t >& inBuffer ) { checkIndex( index ); auto itemName = inputArchive()->itemProperty( index, BitProperty::Path ); - mEditedItems[ index ] = std::make_unique< BufferItem >( inBuffer, itemName.getNativeString() ); + mEditedItems[ index ] = std::make_unique< BufferItem >( inBuffer, itemName.getNativeString() ); //-V108 } void BitArchiveEditor::updateItem( uint32_t index, std::istream& inStream ) { checkIndex( index ); auto itemName = inputArchive()->itemProperty( index, BitProperty::Path ); - mEditedItems[ index ] = std::make_unique< StdInputItem >( inStream, itemName.getNativeString() ); + mEditedItems[ index ] = std::make_unique< StdInputItem >( inStream, itemName.getNativeString() ); //-V108 } void BitArchiveEditor::updateItem( const tstring& itemPath, const tstring& inFile ) { - mEditedItems[ findItem( itemPath ) ] = std::make_unique< FilesystemItem >( tstring_to_path( inFile ), + mEditedItems[ findItem( itemPath ) ] = std::make_unique< FilesystemItem >( tstring_to_path( inFile ), //-V108 tstring_to_path( itemPath ) ); } void BitArchiveEditor::updateItem( const tstring& itemPath, const std::vector< byte_t >& inBuffer ) { - mEditedItems[ findItem( itemPath ) ] = std::make_unique< BufferItem >( inBuffer, itemPath ); + mEditedItems[ findItem( itemPath ) ] = std::make_unique< BufferItem >( inBuffer, itemPath ); //-V108 } void BitArchiveEditor::updateItem( const tstring& itemPath, std::istream& inStream ) { - mEditedItems[ findItem( itemPath ) ] = std::make_unique< StdInputItem >( inStream, itemPath ); + mEditedItems[ findItem( itemPath ) ] = std::make_unique< StdInputItem >( inStream, itemPath ); //-V108 } void BitArchiveEditor::deleteItem( uint32_t index ) { diff --git a/src/bitarchiveitem.cpp b/src/bitarchiveitem.cpp index 99aa3f35..aabdadd9 100644 --- a/src/bitarchiveitem.cpp +++ b/src/bitarchiveitem.cpp @@ -12,7 +12,7 @@ #include "bitarchiveitem.hpp" #include "internal/fsutil.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" // For checking posix file attributes #include diff --git a/src/bitarchivereader.cpp b/src/bitarchivereader.cpp index a065fbeb..ac75fb78 100644 --- a/src/bitarchivereader.cpp +++ b/src/bitarchivereader.cpp @@ -39,7 +39,7 @@ BitArchiveReader::BitArchiveReader( const Bit7zLibrary& lib, auto BitArchiveReader::archiveProperties() const -> map< BitProperty, BitPropVariant > { map< BitProperty, BitPropVariant > result; for ( uint32_t i = kpidNoProperty; i <= kpidCopyLink; ++i ) { - // Yeah, I know, I cast property twice (here and in archiveProperty), but the code is easier to read! + // We cast property twice (here and in archiveProperty), to make the code is easier to read. const auto property = static_cast< BitProperty >( i ); const BitPropVariant propertyValue = archiveProperty( property ); if ( !propertyValue.isEmpty() ) { @@ -54,7 +54,7 @@ auto BitArchiveReader::items() const -> vector< BitArchiveItemInfo > { for ( uint32_t i = 0; i < itemsCount(); ++i ) { BitArchiveItemInfo item( i ); for ( uint32_t j = kpidNoProperty; j <= kpidCopyLink; ++j ) { - // Yeah, I know, I cast property twice (here and in itemProperty), but the code is easier to read! + // We cast property twice (here and in archiveProperty), to make the code is easier to read. const auto property = static_cast< BitProperty >( j ); const auto propertyValue = itemProperty( i, property ); if ( !propertyValue.isEmpty() ) { @@ -90,7 +90,7 @@ auto BitArchiveReader::packSize() const -> uint64_t { auto BitArchiveReader::hasEncryptedItems() const -> bool { /* Note: simple encryption (i.e., not including the archive headers) can be detected only reading - * the properties of the files in the archive, so we search for any encrypted file inside the archive! */ + * the properties of the files in the archive, so we search for any encrypted file inside the archive. */ return std::any_of( cbegin(), cend(), []( const BitArchiveItem& item ) { return !item.isDir() && item.isEncrypted(); } ); diff --git a/src/bitinputarchive.cpp b/src/bitinputarchive.cpp index d98eda32..b6b2464e 100644 --- a/src/bitinputarchive.cpp +++ b/src/bitinputarchive.cpp @@ -26,6 +26,7 @@ #include "internal/fixedbufferextractcallback.hpp" #include "internal/streamextractcallback.hpp" #include "internal/opencallback.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" #ifdef BIT7Z_AUTO_FORMAT @@ -69,7 +70,7 @@ auto BitInputArchive::openArchiveStream( const fs::path& name, IInStream* inStre CMyComPtr< IInArchive > inArchive = mArchiveHandler.library().initInArchive( mArchiveHandler.format() ); #endif // NOTE: CMyComPtr is still needed: if an error occurs, and an exception is thrown, - // the IInArchive object is deleted automatically! + // the IInArchive object is deleted automatically. // Creating open callback for the file auto openCallback = bit7z::make_com< OpenCallback >( mArchiveHandler, name ); @@ -85,9 +86,9 @@ auto BitInputArchive::openArchiveStream( const fs::path& name, IInStream* inStre /* User wanted auto-detection of the format, an extension was detected but opening failed, so we try a more * precise detection by checking the signature. * NOTE: If user specified explicitly a format (i.e., not BitFormat::Auto), this check is not performed, - * and an exception is thrown (next if)! + * and an exception is thrown (next if). * NOTE 2: If signature detection was already performed (detectedBySignature == false), it detected - * a wrong format, no further check can be done, and an exception must be thrown (next if)! */ + * a wrong format, no further check can be done, and an exception must be thrown (next if). */ /* Opening the file might have changed the current file pointer, so we reset it to the beginning of the file * to correctly read the file signature. */ diff --git a/src/bititemsvector.cpp b/src/bititemsvector.cpp index f4ceeba3..6f58ed78 100644 --- a/src/bititemsvector.cpp +++ b/src/bititemsvector.cpp @@ -15,7 +15,7 @@ #include "internal/bufferitem.hpp" #include "internal/fsindexer.hpp" #include "internal/stdinputitem.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" using namespace bit7z; using filesystem::FilesystemItem; diff --git a/src/bitoutputarchive.cpp b/src/bitoutputarchive.cpp index 37e10aee..2af2a11f 100644 --- a/src/bitoutputarchive.cpp +++ b/src/bitoutputarchive.cpp @@ -16,8 +16,8 @@ #include "internal/archiveproperties.hpp" #include "internal/cbufferoutstream.hpp" #include "internal/cmultivolumeoutstream.hpp" -#include "internal/fsutil.hpp" #include "internal/genericinputitem.hpp" +#include "internal/stringutil.hpp" #include "internal/updatecallback.hpp" #include "internal/util.hpp" @@ -35,7 +35,7 @@ BitOutputArchive::BitOutputArchive( const BitAbstractArchiveCreator& creator, co return; } - if ( inArc.empty() ) { // No input file specified, so we are creating a totally new archive! + if ( inArc.empty() ) { // No input file specified, so we are creating a totally new archive. return; } diff --git a/src/bitpropvariant.cpp b/src/bitpropvariant.cpp index 110c9040..1e7f094f 100644 --- a/src/bitpropvariant.cpp +++ b/src/bitpropvariant.cpp @@ -14,11 +14,12 @@ #include "biterror.hpp" #include "bitpropvariant.hpp" #include "internal/dateutil.hpp" +#include "internal/windows.hpp" #if defined( BIT7Z_USE_NATIVE_STRING ) && defined( _WIN32 ) // Windows #define BSTR_TO_TSTRING( bstr ) std::wstring( bstr, ::SysStringLen( bstr ) ) #else -#include "internal/util.hpp" +#include "internal/stringutil.hpp" #define BSTR_TO_TSTRING( bstr ) bit7z::narrow( bstr, SysStringLen( bstr ) ) #endif @@ -156,7 +157,7 @@ BitPropVariant::BitPropVariant( uint64_t value ) noexcept: PROPVARIANT() { BitPropVariant::BitPropVariant( int8_t value ) noexcept: PROPVARIANT() { vt = VT_I1; wReserved1 = 0; - cVal = value; + cVal = static_cast< char >( value ); } BitPropVariant::BitPropVariant( int16_t value ) noexcept: PROPVARIANT() { @@ -331,7 +332,7 @@ auto BitPropVariant::getUInt64() const -> uint64_t { auto BitPropVariant::getInt8() const -> int8_t { switch ( vt ) { case VT_I1: - return cVal; + return static_cast< int8_t >( cVal ); default: // not an 8-bits integer. throw BitException( "BitPropVariant is not an 8-bits integer", make_error_code( BitError::RequestedWrongVariantType ) ); diff --git a/src/bittypes.cpp b/src/bittypes.cpp index 0eaba202..9044fae1 100644 --- a/src/bittypes.cpp +++ b/src/bittypes.cpp @@ -13,7 +13,7 @@ #include "bittypes.hpp" #if defined( _WIN32 ) && !defined( BIT7Z_USE_NATIVE_STRING ) -#include "internal/util.hpp" +#include "internal/stringutil.hpp" #endif namespace bit7z { diff --git a/src/internal/bufferextractcallback.cpp b/src/internal/bufferextractcallback.cpp index 5cdaab64..48201d09 100644 --- a/src/internal/bufferextractcallback.cpp +++ b/src/internal/bufferextractcallback.cpp @@ -14,6 +14,7 @@ #include "internal/bufferextractcallback.hpp" #include "internal/cbufferoutstream.hpp" #include "internal/fs.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" using namespace std; diff --git a/src/internal/bufferitem.cpp b/src/internal/bufferitem.cpp index 998466f1..271dc95e 100644 --- a/src/internal/bufferitem.cpp +++ b/src/internal/bufferitem.cpp @@ -15,6 +15,7 @@ #include "internal/bufferitem.hpp" #include "internal/cbufferinstream.hpp" #include "internal/dateutil.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" using std::vector; diff --git a/src/internal/bufferutil.cpp b/src/internal/bufferutil.cpp index c8f69b30..25a321d9 100644 --- a/src/internal/bufferutil.cpp +++ b/src/internal/bufferutil.cpp @@ -17,44 +17,30 @@ auto bit7z::seek( const buffer_t& buffer, const buffer_t::const_iterator& currentPosition, int64_t offset, uint32_t seekOrigin, - int64_t& newPosition ) -> HRESULT { - int64_t currentIndex; // NOLINT(cppcoreguidelines-init-variables) + uint64_t& newPosition ) -> HRESULT { + uint64_t currentIndex{}; switch ( seekOrigin ) { case STREAM_SEEK_SET: { - currentIndex = 0; break; } case STREAM_SEEK_CUR: { - currentIndex = ( currentPosition - buffer.cbegin() ); + currentIndex = static_cast< uint64_t >( currentPosition - buffer.cbegin() ); break; } case STREAM_SEEK_END: { - currentIndex = ( buffer.cend() - buffer.cbegin() ); + currentIndex = static_cast< uint64_t >( buffer.cend() - buffer.cbegin() ); break; } default: return STG_E_INVALIDFUNCTION; } - // Checking if the sum between currentIndex and offset would result in an integer overflow or underflow. - if ( check_overflow( currentIndex, offset ) ) { - return E_INVALIDARG; - } - - const int64_t newIndex = currentIndex + offset; - - // Making sure the newIndex value is between 0 and mBuffer.size() - if ( newIndex < 0 ) { - return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; - } + RINOK( seek_to_offset( currentIndex, offset ) ) - /* Note: a std::vector's max size can be at most std::numeric_limits< std::ptrdiff_t >::max() - * (see https://en.cppreference.com/w/cpp/container/vector/max_size). - * Since index_t is just an alias for std::ptrdiff_t, the following cast is safe. */ - if ( newIndex > static_cast< index_t >( buffer.size() ) ) { + if ( currentIndex > buffer.size() ) { return E_INVALIDARG; } - newPosition = newIndex; + newPosition = currentIndex; return S_OK; } \ No newline at end of file diff --git a/src/internal/bufferutil.hpp b/src/internal/bufferutil.hpp index 3c0ceaac..357cb19e 100644 --- a/src/internal/bufferutil.hpp +++ b/src/internal/bufferutil.hpp @@ -19,7 +19,7 @@ auto seek( const buffer_t& buffer, const buffer_t::const_iterator& currentPosition, int64_t offset, uint32_t seekOrigin, - int64_t& newPosition ) -> HRESULT; + uint64_t& newPosition ) -> HRESULT; } // namespace bit7z diff --git a/src/internal/cbufferinstream.cpp b/src/internal/cbufferinstream.cpp index 123dff74..e3dafc45 100644 --- a/src/internal/cbufferinstream.cpp +++ b/src/internal/cbufferinstream.cpp @@ -38,11 +38,11 @@ STDMETHODIMP CBufferInStream::Read( void* data, UInt32 size, UInt32* processedSi /* Note: thanks to CBufferInStream::Seek, we can safely assume mCurrentPosition to always be a valid iterator; * so "remaining" will always be > 0 (and casts to unsigned types are safe) */ - size_t remaining = mBuffer.cend() - mCurrentPosition; - if ( remaining > static_cast< size_t >( size ) ) { - /* The remaining buffer still to read is bigger than the buffer size requested by the user, + std::ptrdiff_t remaining = mBuffer.cend() - mCurrentPosition; + if ( cmp_greater( remaining, size ) ) { + /* The remaining buffer still to read is bigger than the read size requested by the user, * so we need to read just a "size" number of bytes. */ - remaining = static_cast< size_t >( size ); + remaining = static_cast< std::ptrdiff_t >( size ); } /* Else, the user requested to read a number of bytes greater than or equal to the number * of remaining bytes to be read from the buffer. @@ -54,7 +54,7 @@ STDMETHODIMP CBufferInStream::Read( void* data, UInt32 size, UInt32* processedSi if ( processedSize != nullptr ) { /* Note: even though on 64-bit systems "remaining" will be a 64-bit unsigned integer (size_t), - * its value cannot be greater than "size", which is a 32-bit unsigned int. Hence, this cast is safe! */ + * its value cannot be greater than "size", which is a 32-bit unsigned int; hence, this cast is safe. */ *processedSize = static_cast< UInt32 >( remaining ); } return S_OK; @@ -62,7 +62,7 @@ STDMETHODIMP CBufferInStream::Read( void* data, UInt32 size, UInt32* processedSi COM_DECLSPEC_NOTHROW STDMETHODIMP CBufferInStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept { - int64_t newIndex{}; + uint64_t newIndex{}; const HRESULT res = seek( mBuffer, mCurrentPosition, offset, seekOrigin, newIndex ); if ( res != S_OK ) { @@ -75,7 +75,7 @@ STDMETHODIMP CBufferInStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* new if ( newPosition != nullptr ) { // Safe cast, since newIndex >= 0 - *newPosition = static_cast< UInt64 >( newIndex ); + *newPosition = newIndex; } return S_OK; diff --git a/src/internal/cbufferoutstream.cpp b/src/internal/cbufferoutstream.cpp index 0315b4a7..dcf02db4 100644 --- a/src/internal/cbufferoutstream.cpp +++ b/src/internal/cbufferoutstream.cpp @@ -32,7 +32,7 @@ STDMETHODIMP CBufferOutStream::SetSize( UInt64 newSize ) noexcept { COM_DECLSPEC_NOTHROW STDMETHODIMP CBufferOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept { - int64_t newIndex{}; + uint64_t newIndex{}; const HRESULT res = seek( mBuffer, mCurrentPosition, offset, seekOrigin, newIndex ); if ( res != S_OK ) { @@ -44,8 +44,7 @@ STDMETHODIMP CBufferOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* ne mCurrentPosition = mBuffer.begin() + static_cast< index_t >( newIndex ); if ( newPosition != nullptr ) { - // Safe cast, since newIndex >=0 0 - *newPosition = static_cast< UInt64 >( newIndex ); + *newPosition = newIndex; } return S_OK; @@ -62,14 +61,14 @@ STDMETHODIMP CBufferOutStream::Write( const void* data, UInt32 size, UInt32* pro } auto oldPos = ( mCurrentPosition - mBuffer.begin() ); - const size_t newPos = oldPos + size; + const size_t newPos = static_cast< size_t >( oldPos ) + static_cast< size_t >( size ); if ( newPos > mBuffer.size() ) { try { mBuffer.resize( newPos ); } catch ( ... ) { return E_OUTOFMEMORY; } - mCurrentPosition = mBuffer.begin() + oldPos; //resize invalidated the old mCurrentPosition iterator + mCurrentPosition = mBuffer.begin() + oldPos; // resize(...) invalidated the old mCurrentPosition iterator } const auto* byteData = static_cast< const byte_t* >( data ); //-V2571 @@ -79,7 +78,7 @@ STDMETHODIMP CBufferOutStream::Write( const void* data, UInt32 size, UInt32* pro return E_OUTOFMEMORY; } - std::advance( mCurrentPosition, size ); + std::advance( mCurrentPosition, clamp_cast< std::ptrdiff_t >( size ) ); if ( processedSize != nullptr ) { *processedSize = size; diff --git a/src/internal/cfileinstream.cpp b/src/internal/cfileinstream.cpp index a50cb37e..65a6c31a 100644 --- a/src/internal/cfileinstream.cpp +++ b/src/internal/cfileinstream.cpp @@ -12,11 +12,7 @@ #include "bitexception.hpp" #include "internal/cfileinstream.hpp" -#include "internal/util.hpp" - -#if defined( _WIN32 ) && defined( __GLIBCXX__ ) && defined( _WIO_DEFINED ) -#include "internal/fsutil.hpp" -#endif +#include "internal/stringutil.hpp" namespace bit7z { @@ -33,10 +29,7 @@ CFileInStream::CFileInStream( const fs::path& filePath ) : CStdInStream( mFileSt void CFileInStream::openFile( const fs::path& filePath ) { mFileStream.open( filePath, std::ios::in | std::ios::binary ); // flawfinder: ignore if ( mFileStream.fail() ) { - //Note: CFileInStream constructor does not directly throw exceptions since it is also used in nothrow functions. - throw BitException( "Failed to open the archive file", - make_hresult_code( HRESULT_FROM_WIN32( ERROR_OPEN_FAILED ) ), - path_to_tstring( filePath ) ); + throw BitException( "Failed to open the archive file", last_error_code(), path_to_tstring( filePath ) ); } } diff --git a/src/internal/cfileoutstream.cpp b/src/internal/cfileoutstream.cpp index e04e48cc..ba7bf7e3 100644 --- a/src/internal/cfileoutstream.cpp +++ b/src/internal/cfileoutstream.cpp @@ -14,11 +14,7 @@ #include "bitexception.hpp" #include "internal/cfileoutstream.hpp" -#include "internal/util.hpp" - -#if defined( _WIN32 ) && defined( __GLIBCXX__ ) && defined( _WIO_DEFINED ) -#include "internal/fsutil.hpp" -#endif +#include "internal/stringutil.hpp" namespace bit7z { @@ -27,16 +23,14 @@ CFileOutStream::CFileOutStream( fs::path filePath, bool createAlways ) std::error_code error; if ( !createAlways && fs::exists( mFilePath, error ) ) { if ( !error ) { - // the call to fs::exists succeeded, but the filePath exists, and this is an error! + // The call to fs::exists succeeded, but the filePath exists, and this is an error. error = std::make_error_code( std::errc::file_exists ); } throw BitException( "Failed to create the output file", error, path_to_tstring( mFilePath ) ); } mFileStream.open( mFilePath, std::ios::binary | std::ios::trunc ); // flawfinder: ignore if ( mFileStream.fail() ) { - throw BitException( "Failed to open the output file", - make_hresult_code( HRESULT_FROM_WIN32( ERROR_OPEN_FAILED ) ), - path_to_tstring( mFilePath ) ); + throw BitException( "Failed to open the output file", last_error_code(), path_to_tstring( mFilePath ) ); } mFileStream.rdbuf()->pubsetbuf( mBuffer.data(), kBufferSize ); diff --git a/src/internal/cfixedbufferoutstream.cpp b/src/internal/cfixedbufferoutstream.cpp index b84e7b49..7c8bc639 100644 --- a/src/internal/cfixedbufferoutstream.cpp +++ b/src/internal/cfixedbufferoutstream.cpp @@ -17,59 +17,11 @@ #include "internal/cfixedbufferoutstream.hpp" #include "internal/util.hpp" -/* Safe integer comparison like in C++20 */ -#ifdef __cpp_if_constexpr - -template< class T, class U > -constexpr auto cmp_less( T first, U second ) noexcept -> bool { - using UT = std::make_unsigned_t< T >; - using UU = std::make_unsigned_t< U >; - if constexpr ( std::is_signed< T >::value == std::is_signed< U >::value ) { - return first < second; - } else if constexpr ( std::is_signed< T >::value ) { - return first < 0 || UT( first ) < second; - } else { - return second >= 0 && first < UU( second ); - } -} - -#else // SFINAE implementation for C++14 - -template< class T, class U > -constexpr auto -cmp_less( T t, U u ) noexcept -> std::enable_if_t< std::is_signed< T >::value == std::is_signed< U >::value, bool > { - return t < u; -} - -template< class T, class U > -constexpr auto -cmp_less( T t, U u ) noexcept -> std::enable_if_t< std::is_signed< T >::value && !std::is_signed< U >::value, bool > { - return t < 0 || std::make_unsigned_t< T >( t ) < u; -} - -template< class T, class U > -constexpr auto -cmp_less( T t, U u ) noexcept -> std::enable_if_t< !std::is_signed< T >::value && std::is_signed< U >::value, bool > { - return u >= 0 && t < std::make_unsigned_t< U >( u ); -} - -#endif - -template< class T, class U > -constexpr auto cmp_greater( T first, U second ) noexcept -> bool { - return cmp_less( second, first ); // NOLINT(*-suspicious-call-argument) -} - -template< class T, class U > -constexpr auto cmp_greater_equal( T first, U second ) noexcept -> bool { - return !cmp_less( first, second ); -} - namespace bit7z { CFixedBufferOutStream::CFixedBufferOutStream( byte_t* buffer, std::size_t size ) : mBuffer( buffer ), mBufferSize( size ), mCurrentPosition( 0 ) { - if ( size == 0 || cmp_greater( size, ( std::numeric_limits< int64_t >::max )() ) ) { + if ( size == 0 ) { throw BitException( "Could not initialize output buffer stream", make_error_code( BitError::InvalidOutputBufferSize ) ); } @@ -82,43 +34,34 @@ STDMETHODIMP CFixedBufferOutStream::SetSize( UInt64 newSize ) noexcept { COM_DECLSPEC_NOTHROW STDMETHODIMP CFixedBufferOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept { - int64_t currentIndex{}; + uint64_t seekIndex{}; switch ( seekOrigin ) { case STREAM_SEEK_SET: { break; } case STREAM_SEEK_CUR: { - currentIndex = mCurrentPosition; + seekIndex = mCurrentPosition; break; } case STREAM_SEEK_END: { - currentIndex = static_cast< int64_t >( mBufferSize ); + seekIndex = mBufferSize; break; } default: return STG_E_INVALIDFUNCTION; } - // Checking if the sum between the currentIndex and offset would result in an integer overflow or underflow - if ( check_overflow( currentIndex, offset ) ) { - return E_INVALIDARG; - } - - const int64_t newIndex = currentIndex + offset; - - // Making sure the newIndex value is between 0 and mBufferSize - 1 - if ( newIndex < 0 ) { - return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; - } + RINOK( seek_to_offset( seekIndex, offset ) ) - if ( cmp_greater_equal( newIndex, mBufferSize ) ) { + // Making sure seekIndex is a valid index within the buffer (i.e., it is less than mBufferSize). + if ( seekIndex >= mBufferSize ) { return E_INVALIDARG; } - mCurrentPosition = newIndex; + mCurrentPosition = clamp_cast< size_t >( seekIndex ); if ( newPosition != nullptr ) { - *newPosition = newIndex; + *newPosition = seekIndex; } return S_OK; @@ -134,11 +77,12 @@ STDMETHODIMP CFixedBufferOutStream::Write( const void* data, UInt32 size, UInt32 return E_FAIL; } - uint32_t writeSize = size; - if ( cmp_greater_equal( size, mBufferSize - mCurrentPosition ) ) { + auto writeSize = static_cast< size_t >( size ); + size_t remainingSize = mBufferSize - mCurrentPosition; // The Seek method ensures mCurrentPosition < mBufferSize. + if ( writeSize > remainingSize ) { /* Writing only to the remaining part of the output buffer! - * Note: since size is an uint32_t, and size >= mBufferSize - mCurrentPosition, the cast is safe! */ - writeSize = static_cast< uint32_t >( mBufferSize - mCurrentPosition ); + * Note: since size is an uint32_t, and size >= mBufferSize - mCurrentPosition, the cast is safe. */ + writeSize = remainingSize; } const auto* byteData = static_cast< const byte_t* >( data ); //-V2571 @@ -152,7 +96,8 @@ STDMETHODIMP CFixedBufferOutStream::Write( const void* data, UInt32 size, UInt32 mCurrentPosition += writeSize; if ( processedSize != nullptr ) { - *processedSize = writeSize; + // Note: writeSize is not greater than size, which is UInt32, so the cast is safe. + *processedSize = static_cast< UInt32 >( writeSize ); } return S_OK; diff --git a/src/internal/cfixedbufferoutstream.hpp b/src/internal/cfixedbufferoutstream.hpp index f46444b9..d4c76d58 100644 --- a/src/internal/cfixedbufferoutstream.hpp +++ b/src/internal/cfixedbufferoutstream.hpp @@ -46,7 +46,7 @@ class CFixedBufferOutStream final : public IOutStream, public CMyUnknownImp { private: byte_t* mBuffer; size_t mBufferSize; - int64_t mCurrentPosition; + size_t mCurrentPosition; }; } // namespace bit7z diff --git a/src/internal/cmultivolumeinstream.cpp b/src/internal/cmultivolumeinstream.cpp index 76633a62..2f888021 100644 --- a/src/internal/cmultivolumeinstream.cpp +++ b/src/internal/cmultivolumeinstream.cpp @@ -93,35 +93,22 @@ STDMETHODIMP CMultiVolumeInStream::Read( void* data, UInt32 size, UInt32* proces COM_DECLSPEC_NOTHROW STDMETHODIMP CMultiVolumeInStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept { - uint64_t originPosition; // NOLINT(cppcoreguidelines-init-variables) + uint64_t seekPosition{}; switch ( seekOrigin ) { case STREAM_SEEK_SET: - originPosition = 0; break; case STREAM_SEEK_CUR: - originPosition = mCurrentPosition; + seekPosition = mCurrentPosition; break; case STREAM_SEEK_END: - originPosition = mTotalSize; + seekPosition = mTotalSize; break; default: return STG_E_INVALIDFUNCTION; } - // Checking if adding the (negative) offset would result in the unsigned wrap around of the current position. - if ( offset < 0 && originPosition < static_cast< uint64_t >( -offset ) ) { - return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; - } - - // Checking if adding the (positive) offset would result in the unsigned wrap around of the current position. - if ( offset > 0 ) { - const auto positiveOffset = static_cast< uint64_t >( offset ); - const uint64_t seekPosition = originPosition + positiveOffset; - if ( seekPosition < originPosition || seekPosition < positiveOffset ) { - return E_INVALIDARG; - } - } - mCurrentPosition = originPosition + offset; + RINOK( seek_to_offset( seekPosition, offset ) ) + mCurrentPosition = seekPosition; if ( newPosition != nullptr ) { *newPosition = mCurrentPosition; diff --git a/src/internal/cmultivolumeoutstream.cpp b/src/internal/cmultivolumeoutstream.cpp index 43061b27..cb23a748 100644 --- a/src/internal/cmultivolumeoutstream.cpp +++ b/src/internal/cmultivolumeoutstream.cpp @@ -103,19 +103,22 @@ STDMETHODIMP CMultiVolumeOutStream::Write( const void* data, UInt32 size, UInt32 COM_DECLSPEC_NOTHROW STDMETHODIMP CMultiVolumeOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept { + uint64_t seekPosition{}; switch ( seekOrigin ) { case STREAM_SEEK_SET: - mAbsoluteOffset = static_cast< uint64_t >( offset ); break; case STREAM_SEEK_CUR: - mAbsoluteOffset += static_cast< uint64_t >( offset ); + seekPosition = mAbsoluteOffset; break; case STREAM_SEEK_END: - mAbsoluteOffset = mFullSize + static_cast< uint64_t >( offset ); + seekPosition = mFullSize; break; default: return STG_E_INVALIDFUNCTION; } + + RINOK( seek_to_offset( seekPosition, offset ) ) + mAbsoluteOffset = seekPosition; mCurrentVolumeOffset = mAbsoluteOffset; if ( newPosition != nullptr ) { *newPosition = mAbsoluteOffset; diff --git a/src/internal/cstdinstream.cpp b/src/internal/cstdinstream.cpp index 14e09df3..78b79a4f 100644 --- a/src/internal/cstdinstream.cpp +++ b/src/internal/cstdinstream.cpp @@ -12,6 +12,7 @@ #include "internal/cstdinstream.hpp" #include "internal/streamutil.hpp" +#include "internal/util.hpp" namespace bit7z { @@ -29,7 +30,7 @@ STDMETHODIMP CStdInStream::Read( void* data, UInt32 size, UInt32* processedSize return S_OK; } - mInputStream.read( static_cast< char* >( data ), size ); // flawfinder: ignore //-V2571 + mInputStream.read( static_cast< char* >( data ), clamp_cast< std::streamsize >( size ) ); // flawfinder: ignore //-V2571 if ( processedSize != nullptr ) { *processedSize = static_cast< uint32_t >( mInputStream.gcount() ); diff --git a/src/internal/cstdoutstream.cpp b/src/internal/cstdoutstream.cpp index 40c065c5..d750132d 100644 --- a/src/internal/cstdoutstream.cpp +++ b/src/internal/cstdoutstream.cpp @@ -14,6 +14,7 @@ #include "internal/cstdoutstream.hpp" #include "internal/streamutil.hpp" +#include "internal/util.hpp" namespace bit7z { @@ -31,7 +32,7 @@ STDMETHODIMP CStdOutStream::Write( const void* data, UInt32 size, UInt32* proces const auto oldPos = mOutputStream.tellp(); - mOutputStream.write( static_cast< const char* >( data ), size ); //-V2571 + mOutputStream.write( static_cast< const char* >( data ), clamp_cast< std::streamsize >( size ) ); //-V2571 if ( processedSize != nullptr ) { *processedSize = static_cast< uint32_t >( mOutputStream.tellp() - oldPos ); diff --git a/src/internal/cvolumeoutstream.cpp b/src/internal/cvolumeoutstream.cpp index d703acf8..f7fad755 100644 --- a/src/internal/cvolumeoutstream.cpp +++ b/src/internal/cvolumeoutstream.cpp @@ -19,8 +19,12 @@ CVolumeOutStream::CVolumeOutStream( const fs::path& volumeName ) COM_DECLSPEC_NOTHROW STDMETHODIMP CVolumeOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept { - RINOK( CStdOutStream::Seek( offset, seekOrigin, newPosition ) ) - mCurrentOffset = offset; + UInt64 pos{}; + RINOK( CStdOutStream::Seek( offset, seekOrigin, &pos ) ) + mCurrentOffset = pos; + if ( newPosition != nullptr ) { + *newPosition = pos; + } return S_OK; } diff --git a/src/internal/dateutil.cpp b/src/internal/dateutil.cpp index 2df3b60f..fecbb0bc 100644 --- a/src/internal/dateutil.cpp +++ b/src/internal/dateutil.cpp @@ -31,8 +31,9 @@ auto FILETIME_to_file_time_type( FILETIME fileTime ) -> fs::file_time_type { return fs::file_time_type{ std::chrono::duration_cast< std::chrono::system_clock::duration >( unixEpoch ) }; } -auto time_to_FILETIME( const std::time_t& timeValue ) -> FILETIME { - const uint64_t timeInSeconds = ( timeValue * 10000000ull ) + 116444736000000000; // NOLINT(*-magic-numbers) +auto time_to_FILETIME( std::time_t timeValue ) -> FILETIME { + // NOLINTNEXTLINE(*-magic-numbers) + const uint64_t timeInSeconds = ( static_cast< uint64_t >( timeValue ) * 10000000ull ) + 116444736000000000; FILETIME fileTime{}; fileTime.dwLowDateTime = static_cast< DWORD >( timeInSeconds ); fileTime.dwHighDateTime = static_cast< DWORD >( timeInSeconds >> 32 ); diff --git a/src/internal/dateutil.hpp b/src/internal/dateutil.hpp index 1cf9fbd8..defc2f19 100644 --- a/src/internal/dateutil.hpp +++ b/src/internal/dateutil.hpp @@ -23,7 +23,7 @@ namespace bit7z { auto FILETIME_to_file_time_type( FILETIME fileTime ) -> fs::file_time_type; -auto time_to_FILETIME( const std::time_t& timeValue ) -> FILETIME; +auto time_to_FILETIME( std::time_t timeValue ) -> FILETIME; #endif diff --git a/src/internal/extractcallback.cpp b/src/internal/extractcallback.cpp index d2c366be..51b30ace 100644 --- a/src/internal/extractcallback.cpp +++ b/src/internal/extractcallback.cpp @@ -15,7 +15,7 @@ #include "bitexception.hpp" #include "internal/extractcallback.hpp" #include "internal/operationcategory.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" namespace bit7z { diff --git a/src/internal/fileextractcallback.cpp b/src/internal/fileextractcallback.cpp index f3d483e1..a34b81a6 100644 --- a/src/internal/fileextractcallback.cpp +++ b/src/internal/fileextractcallback.cpp @@ -13,6 +13,7 @@ #include "bitexception.hpp" #include "internal/fileextractcallback.hpp" #include "internal/fsutil.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" using namespace std; diff --git a/src/internal/formatdetect.cpp b/src/internal/formatdetect.cpp index 70b35c0a..bcf2a41e 100644 --- a/src/internal/formatdetect.cpp +++ b/src/internal/formatdetect.cpp @@ -10,12 +10,14 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ +#include "internal/formatdetect.hpp" + +// Note: the formatdetect.hpp header must be included before this ifdef since the BIT7Z_AUTO_FORMAT +// flag might be manually specified in the bitdefines.hpp header (included by formatdetect.hpp). #ifdef BIT7Z_AUTO_FORMAT #include -#include "internal/formatdetect.hpp" - #if defined(BIT7Z_USE_NATIVE_STRING) && defined(_WIN32) #include // for std::iswdigit #else @@ -340,7 +342,7 @@ struct OffsetSignature { #define bswap64 _byteswap_uint64 #elif defined(__GNUC__) || defined(__clang__) //Note: the versions of gcc and clang that can compile bit7z should also have this builtin, hence there is no need -// for checking the compiler version or using _has_builtin macro! +// for checking the compiler version or using the _has_builtin macro. #define bswap64 __builtin_bswap64 #else static inline uint64_t bswap64( uint64_t x ) { @@ -361,6 +363,10 @@ auto read_signature( IInStream* stream, uint32_t size ) noexcept -> uint64_t { return bswap64( signature ); } +// Note: the left shifting of the signature mask might overflow, but it is intentional, so we suppress the sanitizer. +#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ >= 12) +__attribute__((no_sanitize ("unsigned-shift-base"))) +#endif auto detect_format_from_signature( IInStream* stream ) -> const BitInFormat& { constexpr auto kSignatureSize = 8U; constexpr auto kBaseSignatureMask = 0xFFFFFFFFFFFFFFFFULL; @@ -375,7 +381,7 @@ auto detect_format_from_signature( IInStream* stream ) -> const BitInFormat& { return *format; } signatureMask <<= kByteShift; // left shifting the mask of one byte, so that - fileSignature &= signatureMask; // the least significant i bytes are masked (set to 0) + fileSignature &= signatureMask; // the least significant i bytes are masked (set to 0) } static const OffsetSignature commonSignaturesWithOffset[] = { // NOLINT(*-avoid-c-arrays) @@ -385,7 +391,7 @@ auto detect_format_from_signature( IInStream* stream ) -> const BitInFormat& { { 0x436F6D7072657373, 0x10, 8, BitFormat::CramFS }, // Compress { 0x7F10DABE00000000, 0x40, 4, BitFormat::VDI }, // 0x7F 0x10 0xDA 0xBE { 0x7573746172000000, 0x101, 5, BitFormat::Tar }, // ustar - // Note: since GPT files contain also the FAT signature, GPT must be checked before! + /* Note: since GPT files contain also the FAT signature, we must check the GPT signature before the FAT one. */ { 0x4546492050415254, 0x200, 8, BitFormat::GPT }, // EFI 0x20 PART { 0x55AA000000000000, 0x1FE, 2, BitFormat::Fat }, // U 0xAA { 0x4244000000000000, 0x400, 2, BitFormat::Hfs }, // BD @@ -446,7 +452,7 @@ auto detect_format_from_signature( IInStream* stream ) -> const BitInFormat& { # define is_digit(ch) std::iswdigit(ch) != 0 const auto to_lower = std::towlower; #else -inline auto is_digit( unsigned char character ) -> bool { +inline auto is_digit( char character ) -> bool { return std::isdigit( character ) != 0; } diff --git a/src/internal/fsitem.cpp b/src/internal/fsitem.cpp index 77082203..d7fa9cd7 100644 --- a/src/internal/fsitem.cpp +++ b/src/internal/fsitem.cpp @@ -16,6 +16,7 @@ #include "internal/cfileinstream.hpp" #include "internal/csymlinkinstream.hpp" #include "internal/fsitem.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" namespace bit7z { // NOLINT(modernize-concat-nested-namespaces) diff --git a/src/internal/fsutil.cpp b/src/internal/fsutil.cpp index 510c41c2..50abd499 100644 --- a/src/internal/fsutil.cpp +++ b/src/internal/fsutil.cpp @@ -23,7 +23,7 @@ #endif #include "internal/fsutil.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" using namespace std; @@ -145,7 +145,7 @@ auto restore_symlink( const std::string& name ) -> bool { } // Shrinking the path string to its actual size. - linkPath.resize( ifs.gcount() ); + linkPath.resize( static_cast< size_t >( ifs.gcount() ) ); // No need to keep the file open. ifs.close(); @@ -158,17 +158,17 @@ auto restore_symlink( const std::string& name ) -> bool { return !error && symlink( linkPath.c_str(), name.c_str() ) == 0; } -static const mode_t global_umask = []() noexcept { +static const mode_t global_umask = []() noexcept -> mode_t { // Getting and setting the current umask. // Note: flawfinder warns about umask with the mask set to 0; // however, we use it only to read the current umask, - // then we restore the old value, hence we can ignore the warning! + // then we restore the old value, hence we can ignore the warning. const mode_t currentUmask{ umask( 0 ) }; // flawfinder: ignore // Restoring the umask. umask( currentUmask ); // flawfinder: ignore - return static_cast( fs::perms::all ) & ( ~currentUmask ); + return static_cast< mode_t >( static_cast< int >( fs::perms::all ) & ( ~currentUmask ) ); }(); #endif @@ -212,7 +212,7 @@ auto fsutil::set_file_attributes( const fs::path& filePath, DWORD attributes ) n } else if ( S_ISLNK( fileStat.st_mode ) ) { return true; } else if ( !S_ISDIR( fileStat.st_mode ) && ( attributes & FILE_ATTRIBUTE_READONLY ) != 0 ) { - fileStat.st_mode &= ~( S_IWUSR | S_IWGRP | S_IWOTH ); + fileStat.st_mode &= static_cast< mode_t >( ~( S_IWUSR | S_IWGRP | S_IWOTH ) ); } const fs::perms filePermissions = static_cast< fs::perms >( fileStat.st_mode & global_umask ) & fs::perms::mask; @@ -268,8 +268,9 @@ auto fsutil::get_file_attributes_ex( const fs::path& filePath, if ( ( statInfo.st_mode & S_IWUSR ) == 0 ) { fileMetadata.dwFileAttributes |= FILE_ATTRIBUTE_READONLY; } - constexpr auto kMask = 0xFFFF; - fileMetadata.dwFileAttributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + ( ( statInfo.st_mode & kMask ) << 16 ); + constexpr auto kMask = 0xFFFFu; + std::uint32_t unixAttributes = ( ( statInfo.st_mode & kMask ) << 16u ); + fileMetadata.dwFileAttributes |= FILE_ATTRIBUTE_UNIX_EXTENSION + unixAttributes; // File times fileMetadata.ftCreationTime = time_to_FILETIME( statInfo.st_ctime ); diff --git a/src/internal/genericinputitem.cpp b/src/internal/genericinputitem.cpp index 3555507d..0947db9e 100644 --- a/src/internal/genericinputitem.cpp +++ b/src/internal/genericinputitem.cpp @@ -11,7 +11,7 @@ */ #include "internal/genericinputitem.hpp" -#include "util.hpp" +#include "internal/stringutil.hpp" namespace bit7z { diff --git a/src/internal/hresultcategory.cpp b/src/internal/hresultcategory.cpp index 77cb7c94..3906b274 100644 --- a/src/internal/hresultcategory.cpp +++ b/src/internal/hresultcategory.cpp @@ -28,7 +28,12 @@ auto HRESULTCategory::message( int errorValue ) const -> std::string { FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK, - nullptr, errorValue, 0, reinterpret_cast< LPSTR >( &messageBuffer ), 0, nullptr ); + nullptr, + static_cast< DWORD >( errorValue ), + 0, + reinterpret_cast< LPSTR >( &messageBuffer ), // NOLINT(*-pro-type-reinterpret-cast) + 0, + nullptr ); if ( msgSize == 0 ) { return "Unknown error"; } @@ -39,7 +44,7 @@ auto HRESULTCategory::message( int errorValue ) const -> std::string { LocalFree( messageBuffer ); return errorMessage; #else - // Note: same messages returned by FormatMessageA on Windows platform. + // Note: same messages returned by FormatMessageA on Windows. switch ( static_cast< HRESULT >( errorValue ) ) { case E_ABORT: return "Operation aborted"; diff --git a/src/internal/opencallback.cpp b/src/internal/opencallback.cpp index cb0502ca..c5eb1f6c 100644 --- a/src/internal/opencallback.cpp +++ b/src/internal/opencallback.cpp @@ -13,6 +13,7 @@ #include "bitexception.hpp" #include "internal/cfileinstream.hpp" #include "internal/opencallback.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" namespace bit7z { diff --git a/src/internal/renameditem.cpp b/src/internal/renameditem.cpp index 4e57971e..fc68fe33 100644 --- a/src/internal/renameditem.cpp +++ b/src/internal/renameditem.cpp @@ -13,7 +13,7 @@ #include "internal/dateutil.hpp" #include "internal/fsutil.hpp" #include "internal/renameditem.hpp" -#include "internal/util.hpp" +#include "internal/stringutil.hpp" namespace bit7z { diff --git a/src/internal/stdinputitem.cpp b/src/internal/stdinputitem.cpp index 826eda1c..e1e56fc4 100644 --- a/src/internal/stdinputitem.cpp +++ b/src/internal/stdinputitem.cpp @@ -15,6 +15,7 @@ #include "internal/cstdinstream.hpp" #include "internal/dateutil.hpp" #include "internal/stdinputitem.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" using std::istream; diff --git a/src/internal/util.cpp b/src/internal/stringutil.cpp similarity index 94% rename from src/internal/util.cpp rename to src/internal/stringutil.cpp index 761136a3..9a0a3f44 100644 --- a/src/internal/util.cpp +++ b/src/internal/stringutil.cpp @@ -12,9 +12,10 @@ #include -#include "internal/util.hpp" +#include "internal/stringutil.hpp" #ifdef _WIN32 +#include #ifdef BIT7Z_USE_SYSTEM_CODEPAGE #define CODEPAGE CP_ACP #define CODEPAGE_WC_FLAGS WC_NO_BEST_FIT_CHARS @@ -57,7 +58,7 @@ auto narrow( const wchar_t* wideString, size_t size ) -> std::string { return ""; } - std::string result( narrowStringSize, 0 ); + std::string result( static_cast< std::string::size_type >( narrowStringSize ), 0 ); WideCharToMultiByte( CODEPAGE, CODEPAGE_WC_FLAGS, wideString, @@ -90,7 +91,7 @@ auto widen( const std::string& narrowString ) -> std::wstring { return L""; } - std::wstring result( wideStringSize, 0 ); + std::wstring result( static_cast< std::wstring::size_type >( wideStringSize ), 0 ); MultiByteToWideChar( CODEPAGE, 0, narrowString.c_str(), diff --git a/src/internal/stringutil.hpp b/src/internal/stringutil.hpp new file mode 100644 index 00000000..2ac190bb --- /dev/null +++ b/src/internal/stringutil.hpp @@ -0,0 +1,79 @@ +/* + * bit7z - A C++ static library to interface with the 7-zip shared libraries. + * Copyright (c) 2014-2023 Riccardo Ostani - All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +#ifndef STRINGUTIL_HPP +#define STRINGUTIL_HPP + +#include "bittypes.hpp" +#include "internal/fsutil.hpp" + +namespace bit7z { + +#if defined( BIT7Z_USE_NATIVE_STRING ) && defined( _WIN32 ) +// On Windows, with native strings enabled, strings are already wide! +# define WIDEN( tstr ) tstr +#else +# define WIDEN( tstr ) bit7z::widen(tstr) + +auto narrow( const wchar_t* wideString, size_t size ) -> std::string; + +auto widen( const std::string& narrowString ) -> std::wstring; +#endif + +inline auto path_to_tstring( const fs::path& path ) -> tstring { + /* In an ideal world, we should only use fs::path's string< tchar >() function for converting a path to a tstring. + * However, MSVC converts paths to std::string using the system codepage instead of UTF-8, + * which is the default encoding of bit7z. */ +#if defined( _WIN32 ) && defined( BIT7Z_USE_NATIVE_STRING ) + return path.wstring(); +#elif defined( _WIN32 ) && defined( BIT7Z_USE_SYSTEM_CODEPAGE ) + /* If we encounter a path with Unicode characters, MSVC will throw an exception + * while converting from a fs::path to std::string if any character is invalid in the system codepage. + * Hence, here we use bit7z's own string conversion function, which substitutes invalid Unicode characters + * with '?' characters. */ + const auto& native_path = path.native(); + return narrow( native_path.c_str(), native_path.size() ); +#else + return path.u8string(); +#endif +} + +#if defined( _MSC_VER ) && !defined( BIT7Z_USE_NATIVE_STRING ) && !defined( BIT7Z_USE_SYSTEM_CODEPAGE ) +#define PATH_FROM_TSTRING( str ) fs::u8path( str ) +#else +#define PATH_FROM_TSTRING( str ) fs::path{ str } +#endif + +inline auto tstring_to_path( const tstring& str ) -> fs::path { +#if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS ) + auto result = PATH_FROM_TSTRING( str ); + if ( filesystem::fsutil::should_format_long_path( result ) ) { + result = filesystem::fsutil::format_long_path( result ); + } + return result; +#else + // By default, MSVC treats strings as encoded using the system codepage, but bit7z uses UTF-8. + return PATH_FROM_TSTRING( str ); +#endif +} + +inline auto path_to_wide_string( const fs::path& path ) -> std::wstring { +#if defined( _MSC_VER ) || !defined( BIT7Z_USE_STANDARD_FILESYSTEM ) + return path.wstring(); +#else + /* On some compilers and platforms (e.g., GCC before v12.3), + * the direct conversion of the fs::path to wstring might throw an exception due to unicode characters. + * So we simply convert to tstring, and then widen it if necessary. */ + return WIDEN( path.string< tchar >() ); +#endif +} + +} // namespace bit7z + +#endif //STRINGUTIL_HPP diff --git a/src/internal/updatecallback.cpp b/src/internal/updatecallback.cpp index a3a1109a..0e035aab 100644 --- a/src/internal/updatecallback.cpp +++ b/src/internal/updatecallback.cpp @@ -12,6 +12,7 @@ #include "internal/cfileoutstream.hpp" #include "internal/updatecallback.hpp" +#include "internal/stringutil.hpp" #include "internal/util.hpp" namespace bit7z { diff --git a/src/internal/util.hpp b/src/internal/util.hpp index 446e432c..b59dc38e 100644 --- a/src/internal/util.hpp +++ b/src/internal/util.hpp @@ -16,86 +16,141 @@ #include #include "internal/com.hpp" -#include "internal/fs.hpp" - -#if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS ) -#include "internal/fsutil.hpp" -#endif namespace bit7z { -#if defined( BIT7Z_USE_NATIVE_STRING ) && defined( _WIN32 ) -// On Windows, with native strings enabled, strings are already wide! -# define WIDEN( tstr ) tstr -#else -# define WIDEN( tstr ) bit7z::widen(tstr) - -auto narrow( const wchar_t* wideString, size_t size ) -> std::string; +constexpr inline auto check_overflow( int64_t position, int64_t offset ) noexcept -> bool { + return ( ( offset > 0 ) && ( position > ( ( std::numeric_limits< int64_t >::max )() - offset ) ) ) || + ( ( offset < 0 ) && ( position < ( ( std::numeric_limits< int64_t >::min )() - offset ) ) ); +} -auto widen( const std::string& narrowString ) -> std::wstring; +#if defined(__clang__) && defined(__clang_major__) && (__clang_major__ >= 4) +__attribute__((no_sanitize ("unsigned-integer-overflow"))) #endif +inline auto seek_to_offset( uint64_t& position, int64_t offset ) noexcept -> HRESULT { + // Checking if adding the offset would result in the unsigned wrap around of the current position. + if ( offset < 0 ) { + if ( offset == std::numeric_limits< int64_t >::min() ) { + return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; + } + const auto positiveOffset = static_cast< uint64_t >( -offset ); + if ( position < positiveOffset ) { + return HRESULT_WIN32_ERROR_NEGATIVE_SEEK; + } + position -= positiveOffset; + } else if ( offset > 0 ) { + const auto positiveOffset = static_cast< uint64_t >( offset ); + const uint64_t seekPosition = position + positiveOffset; + if ( seekPosition < position ) { + return E_INVALIDARG; + } + position = seekPosition; + } else { + // No action needed + } + return S_OK; +} -inline auto path_to_tstring( const fs::path& path ) -> tstring { - /* In an ideal world, we should only use fs::path's string< tchar >() function for converting a path to a tstring. - * However, MSVC converts paths to std::string using the system codepage instead of UTF-8, - * which is the default encoding of bit7z. */ -#if defined( _WIN32 ) && defined( BIT7Z_USE_NATIVE_STRING ) - return path.wstring(); -#elif defined( _WIN32 ) && defined( BIT7Z_USE_SYSTEM_CODEPAGE ) - /* If we encounter a path with Unicode characters, MSVC will throw an exception - * while converting from a fs::path to std::string if any character is invalid in the system codepage. - * Hence, here we use bit7z's own string conversion function, which substitutes invalid Unicode characters - * with '?' characters. */ - const auto& native_path = path.native(); - return narrow( native_path.c_str(), native_path.size() ); -#else - return path.u8string(); -#endif +/* Safe integer comparison like in C++20 */ +#ifdef __cpp_if_constexpr + +template< class T, class U > +constexpr auto cmp_less( T first, U second ) noexcept -> bool { + using UT = std::make_unsigned_t< T >; + using UU = std::make_unsigned_t< U >; + if constexpr ( std::is_signed< T >::value == std::is_signed< U >::value ) { + return first < second; + } else if constexpr ( std::is_signed< T >::value ) { + return ( first < 0 ) || ( UT( first ) < second ); + } else { + return ( second >= 0 ) && ( first < UU( second ) ); + } } -#if defined( _MSC_VER ) && !defined( BIT7Z_USE_NATIVE_STRING ) && !defined( BIT7Z_USE_SYSTEM_CODEPAGE ) -#define PATH_FROM_TSTRING( str ) fs::u8path( str ) -#else -#define PATH_FROM_TSTRING( str ) fs::path{ str } -#endif +#else // SFINAE implementation for C++14 -inline auto tstring_to_path( const tstring& str ) -> fs::path { -#if defined( _WIN32 ) && defined( BIT7Z_AUTO_PREFIX_LONG_PATHS ) - auto result = PATH_FROM_TSTRING( str ); - if ( filesystem::fsutil::should_format_long_path( result ) ) { - result = filesystem::fsutil::format_long_path( result ); - } - return result; -#else - // By default, MSVC treats strings as encoded using the system codepage, but bit7z uses UTF-8. - return PATH_FROM_TSTRING( str ); -#endif +template< class T, class U, std::enable_if_t< std::is_signed< T >::value == std::is_signed< U >::value, int > = 0 > +constexpr auto cmp_less( T first, U second ) noexcept -> bool { + return first < second; +} + +template< class T, class U, std::enable_if_t< std::is_signed< T >::value && !std::is_signed< U >::value, int > = 0 > +constexpr auto cmp_less( T first, U second ) noexcept -> bool { + return ( first < 0 ) || ( std::make_unsigned_t< T >( first ) < second ); +} + +template< class T, class U, std::enable_if_t< !std::is_signed< T >::value && std::is_signed< U >::value, int > = 0 > +constexpr auto cmp_less( T first, U second ) noexcept -> bool { + return ( second >= 0 ) && ( first < std::make_unsigned_t< U >( second ) ); } -inline auto path_to_wide_string( const fs::path& path ) -> std::wstring { -#if defined( _MSC_VER ) || !defined( BIT7Z_USE_STANDARD_FILESYSTEM ) - return path.wstring(); -#else - /* On some compilers and platforms (e.g., GCC before v12.3), - * the direct conversion of the fs::path to wstring might throw an exception due to unicode characters. - * So we simply convert to tstring, and then widen it if necessary. */ - return WIDEN( path.string< tchar >() ); #endif + +template< class T, class U > +constexpr auto cmp_greater( T first, U second ) noexcept -> bool { + return cmp_less( second, first ); // NOLINT(*-suspicious-call-argument) } -constexpr inline auto check_overflow( int64_t position, int64_t offset ) noexcept -> bool { - return ( ( offset > 0 ) && ( position > ( ( std::numeric_limits< int64_t >::max )() - offset ) ) ) || - ( ( offset < 0 ) && ( position < ( ( std::numeric_limits< int64_t >::min )() - offset ) ) ); +template< class T, class U > +constexpr auto cmp_greater_equal( T first, U second ) noexcept -> bool { + return !cmp_less( first, second ); } template< bool B > using bool_constant = std::integral_constant< bool, B >; // like C++17's std::bool_constant +// TODO: Use a variable template like have_same_signedness_v; supported from GCC 5+, MSVC 2015 Update 2 +template< typename T, typename U > +using have_same_signedness = bool_constant< std::is_signed< T >::value == std::is_signed< U >::value >; + +template< typename T, typename U > +using are_both_signed = bool_constant< std::is_signed< T >::value && std::is_signed< U >::value >; + +template< typename T, typename U > +using are_both_unsigned = bool_constant< std::is_unsigned< T >::value && std::is_unsigned< U >::value >; + +template< typename T, typename U > +using are_both_integral = bool_constant< std::is_integral< T >::value && std::is_integral< U >::value >; + +template< typename To, typename From > +using is_narrower_signed = bool_constant< are_both_signed< To, From >::value && sizeof( To ) < sizeof( From ) >; + +template< typename To, typename From > +inline auto clamp_cast( From value ) noexcept -> std::enable_if_t< are_both_integral< To, From >::value && + ( is_narrower_signed< To, From >::value || + !have_same_signedness< From, To >::value ), To > { + constexpr auto kMaxValue = std::numeric_limits< To >::max(); + if ( cmp_greater( value, kMaxValue ) ) { + return kMaxValue; + } + + constexpr auto kMinValue = std::numeric_limits< To >::min(); + if ( cmp_less( value, kMinValue ) ) { + return kMinValue; + } + + return static_cast< To >( value ); +} + +template< typename To, typename From > +inline auto clamp_cast( From value ) noexcept -> std::enable_if_t< are_both_unsigned< From, To >::value && + sizeof( To ) < sizeof( From ), To > { + constexpr auto kMaxValue = std::numeric_limits< To >::max(); + return value > kMaxValue ? kMaxValue : static_cast< To >( value ); +} + +template< typename To, typename From > +inline auto clamp_cast( From value ) noexcept -> std::enable_if_t< are_both_integral< To, From >::value && + have_same_signedness< To, From >::value && + sizeof( To ) >= sizeof( From ), To > { + return static_cast< To >( value ); +} + template< typename T, typename I = T > using is_com_type = bool_constant< std::is_base_of< CMyUnknownImp, T >::value && std::is_base_of< I, T >::value >; template< typename T, typename I = T, class... Args > -inline auto make_com( Args&& ... args ) -> CMyComPtr< typename std::enable_if< is_com_type< T, I >::value, I >::type > { +inline auto make_com( Args&& ... args ) -> CMyComPtr< std::enable_if_t< is_com_type< T, I >::value, I > > { return CMyComPtr< I >( new T( std::forward< Args >( args )... ) ); //-V2511 } diff --git a/src/internal/windows.cpp b/src/internal/windows.cpp index 729bfd6c..9304aed7 100644 --- a/src/internal/windows.cpp +++ b/src/internal/windows.cpp @@ -13,6 +13,7 @@ #ifndef _WIN32 #include "bittypes.hpp" +#include "internal/util.hpp" #include "internal/windows.hpp" #include @@ -50,10 +51,8 @@ using bstr_prefix_t = uint32_t; * - We use C allocation functions instead of "new" since we must be able to also free BSTR objects * allocated by 7-zip (which uses malloc). Never mix new/delete and malloc/free. * - We use calloc instead of malloc, so that we do not have to manually add the termination character at the end. - * - The length parameter is an uint64_t, instead of the UINT parameter used in the WinAPI interface. - * This allows avoiding unsigned integer wrap around in SysAllocStringLen. * */ -auto AllocStringBuffer( LPCSTR str, uint64_t byteLength ) -> BSTR { +auto AllocStringBuffer( LPCSTR str, uint32_t byteLength ) -> BSTR { // Maximum value that can be stored in the BSTR byteLength prefix. constexpr auto kMaxPrefixValue = std::numeric_limits< bstr_prefix_t >::max(); @@ -92,9 +91,10 @@ auto AllocStringBuffer( LPCSTR str, uint64_t byteLength ) -> BSTR { } auto SysAllocStringLen( const OLECHAR* str, UINT length ) -> BSTR { - auto byteLength = static_cast< uint64_t >( length ) * sizeof( OLECHAR ); + auto byteLength = length * sizeof( OLECHAR ); + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) - return AllocStringBuffer( reinterpret_cast< LPCSTR >( str ), byteLength ); + return AllocStringBuffer( reinterpret_cast< LPCSTR >( str ), clamp_cast< std::uint32_t >( byteLength ) ); } auto SysAllocStringByteLen( LPCSTR str, UINT length ) -> BSTR { @@ -123,7 +123,7 @@ auto SysStringByteLen( BSTR bstrString ) -> UINT { // NOLINT(readability-non-con auto SysStringLen( BSTR bstrString ) -> UINT { // NOLINT(readability-non-const-parameter) // Same as SysStringByteLen, but we count how many OLECHARs are stored in the BSTR. - return SysStringByteLen( bstrString ) / sizeof( OLECHAR ); + return SysStringByteLen( bstrString ) / static_cast< UINT >( sizeof( OLECHAR ) ); } #endif \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index e0ebbe9f..7956ebc8 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -16,6 +16,7 @@ set( SOURCE_FILES # public API test sources set( PUBLIC_API_SOURCE_FILES src/test_bit7zlibrary.cpp + src/test_bitabstractarchivecreator.cpp src/test_bitarchiveeditor.cpp src/test_bitarchivereader.cpp src/test_bitarchivewriter.cpp @@ -36,6 +37,7 @@ set( INTERNAL_API_SOURCE_FILES src/test_dateutil.cpp src/test_fsutil.cpp src/test_util.cpp + src/test_stringutil.cpp src/test_windows.cpp src/test_formatdetect.cpp ) @@ -51,7 +53,7 @@ if( BIT7Z_TESTS_FILESYSTEM ) include( FetchContent ) FetchContent_Declare( bit7z-test-data GIT_REPOSITORY https://github.com/rikyoz/bit7z-test-data.git - GIT_TAG a2966c00854ecadb4dfdadbb30b48e7eeeb8772b + GIT_TAG abc1adc273dd8dd17f55969838811f98872d77b8 GIT_SHALLOW ON SOURCE_DIR ${BIT7Z_TESTS_DATA_DIR} ) FetchContent_MakeAvailable( bit7z-test-data ) @@ -129,6 +131,9 @@ endif() if( MSVC ) target_compile_options( ${TESTS_TARGET} PRIVATE /utf-8 ) target_compile_options( ${TESTS_TARGET_PUBLIC} PRIVATE /utf-8 ) +else() + target_compile_options( ${TESTS_TARGET} PRIVATE -Wno-deprecated -Wno-deprecated-declarations ) + target_compile_options( ${TESTS_TARGET_PUBLIC} PRIVATE -Wno-deprecated -Wno-deprecated-declarations ) endif() if( WIN32 ) @@ -148,8 +153,6 @@ if( WIN32 ) target_sources( ${TESTS_TARGET_PUBLIC} PRIVATE res/encodingLegacy.${RES_EXTENSION} ) endif() -include( cmake/Sanitizers.cmake ) - # Catch2 include( cmake/Catch2.cmake ) target_link_libraries( ${TESTS_TARGET} PRIVATE Catch2::Catch2 ) @@ -157,5 +160,4 @@ target_link_libraries( ${TESTS_TARGET_PUBLIC} PRIVATE Catch2::Catch2 ) include( CTest ) include( Catch ) -catch_discover_tests( ${TESTS_TARGET} ) -catch_discover_tests( ${TESTS_TARGET_PUBLIC} ) \ No newline at end of file +catch_discover_tests( ${TESTS_TARGET} ) \ No newline at end of file diff --git a/tests/cmake/Sanitizers.cmake b/tests/cmake/Sanitizers.cmake deleted file mode 100644 index 1c917a30..00000000 --- a/tests/cmake/Sanitizers.cmake +++ /dev/null @@ -1,58 +0,0 @@ -macro( add_sanitizer name ) - set( CMAKE_REQUIRED_FLAGS "-fsanitize=${name}" ) - check_cxx_compiler_flag( -fsanitize=${name} COMPILER_SUPPORTS_SANITIZE_${name} ) - if( COMPILER_SUPPORTS_SANITIZE_${name} ) - message( STATUS "Supported sanitizer: ${name}" ) - set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize=${name}" ) - else() - message( STATUS "Unsupported sanitizer: ${name}" ) - endif() - unset( CMAKE_REQUIRED_FLAGS ) -endmacro() - -option( BIT7Z_TESTS_NO_SANITIZERS "" OFF ) - -if( MSVC ) - set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} /RTCsu /analyze /guard:cf" ) -elseif( NOT WIN32 AND NOT BIT7Z_TESTS_NO_SANITIZERS ) # GCC/Clang on Linux/macOS (i.e., not MinGW) - set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-omit-frame-pointer" ) - - if( CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "6.0.0" ) - set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -static-libsan" ) - endif() - - # Use the highest stack protection level supported by the target toolchain. - include( CheckCXXCompilerFlag ) - check_cxx_compiler_flag( -fstack-protector-strong COMPILER_SUPPORT_STRONG_STACK_PROTECTOR ) - check_cxx_compiler_flag( -fstack-protector COMPILER_SUPPORT_STACK_PROTECTOR ) - if( COMPILER_SUPPORT_STRONG_STACK_PROTECTOR ) - set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector-strong" ) - elseif( COMPILER_SUPPORT_STACK_PROTECTOR ) - set( CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_DEBUG} -fstack-protector" ) - endif() - - # For some reasons, using the leak sanitizer on Clang 14 gives lots of 'undefined reference' - # issues during linking... ¯\_(ツ)_/¯ - if( NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" ) - add_sanitizer( leak ) - endif() - - # For some reasons, the address sanitizer gives a CHECK failed error on versions of Clang before the 3.9 - # Also, on old versions of GCC the sanitizer give some false positives. - if( ( NOT CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "5.0" ) OR - ( CMAKE_CXX_COMPILER_ID MATCHES "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL "3.9.0" ) ) - add_sanitizer( address ) - check_cxx_compiler_flag( -fsanitize-address-use-after-scope COMPILER_SUPPORT_SANITIZE_USE_AFTER_SCOPE ) - if( COMPILER_SUPPORT_SANITIZE_USE_AFTER_SCOPE ) - set( CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fsanitize-address-use-after-scope" ) - endif() - endif() - - add_sanitizer( float-divide-by-zero ) - add_sanitizer( implicit-conversion ) - add_sanitizer( integer ) - add_sanitizer( local-bounds ) - add_sanitizer( nullability ) - add_sanitizer( undefined ) -endif() - diff --git a/tests/src/test_bitabstractarchivecreator.cpp b/tests/src/test_bitabstractarchivecreator.cpp new file mode 100644 index 00000000..7819bd0a --- /dev/null +++ b/tests/src/test_bitabstractarchivecreator.cpp @@ -0,0 +1,550 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/* + * bit7z - A C++ static library to interface with the 7-zip shared libraries. + * Copyright (c) 2014-2023 Riccardo Ostani - All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include + +#include +#include +#include +#include + +#include "utils/shared_lib.hpp" + +using namespace bit7z; +using bit7z::Bit7zLibrary; +using bit7z::BitArchiveWriter; +using bit7z::BitFileCompressor; +using bit7z::BitMemCompressor; +using bit7z::BitStreamCompressor; +using bit7z::BitInOutFormat; + +struct TestOutputFormat { + const char* name; + const BitInOutFormat& format; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) +}; + +using CreatorTypes = std::tuple< BitArchiveWriter, BitFileCompressor, BitMemCompressor, BitStreamCompressor >; + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setPassword(...) / password() / cryptHeaders()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.password().empty() ); + REQUIRE( !compressor.cryptHeaders() ); + + compressor.setPassword( BIT7Z_STRING( "ciao" ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "ciao" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + compressor.setPassword( BIT7Z_STRING( "mondo" ), true ); + REQUIRE( compressor.password() == BIT7Z_STRING( "mondo" ) ); + REQUIRE( compressor.cryptHeaders() ); + + compressor.setPassword( BIT7Z_STRING( "hello" ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "hello" ) ); + REQUIRE( compressor.cryptHeaders() ); + + compressor.setPassword( BIT7Z_STRING( "" ) ); + REQUIRE( compressor.password().empty() ); + REQUIRE( !compressor.cryptHeaders() ); + + BitAbstractArchiveHandler& handler = compressor; + handler.setPassword( BIT7Z_STRING( "world!" ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "world!" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + compressor.setPassword( BIT7Z_STRING( "foo" ), true ); + handler.setPassword( BIT7Z_STRING( "" ) ); + REQUIRE( compressor.password().empty() ); + REQUIRE( !compressor.cryptHeaders() ); +} + +#ifndef BIT7Z_DISABLE_ZIP_ASCII_PWD_CHECK +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator:" + "setPassword(...) with a non-ASCII string should throw when using the ZIP format", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor{ lib, BitFormat::Zip }; + REQUIRE( compressor.password().empty() ); + REQUIRE( !compressor.cryptHeaders() ); + + REQUIRE_NOTHROW( compressor.setPassword( BIT7Z_STRING( "password" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + REQUIRE_THROWS( compressor.setPassword( BIT7Z_STRING( "contraseña" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + // The carriage return is an ASCII character, but 7-zip doesn't support + // also non-printable ASCII characters for ZIP passwords. + REQUIRE_THROWS( compressor.setPassword( BIT7Z_STRING( "car\riage" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + // The unit separator character is the last non-printable ASCII character. + REQUIRE_THROWS( compressor.setPassword( BIT7Z_STRING( "unit\x1Fseparator" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + // The DEL character is not supported by bit7z for ZIP passwords. + REQUIRE_THROWS( compressor.setPassword( BIT7Z_STRING( "del\U0000007F" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + // The space character is the first printable ASCII character supported. + REQUIRE_NOTHROW( compressor.setPassword( BIT7Z_STRING( "password with spaces" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password with spaces" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + // The tilde character is the last printable ASCII character supported. + REQUIRE_NOTHROW( compressor.setPassword( BIT7Z_STRING( "password~with~tilde" ) ) ); + REQUIRE( compressor.password() == BIT7Z_STRING( "password~with~tilde" ) ); + REQUIRE( !compressor.cryptHeaders() ); + + // Resetting the password + REQUIRE_NOTHROW( compressor.setPassword( BIT7Z_STRING( "" ) ) ); + REQUIRE( compressor.password().empty() ); + REQUIRE( !compressor.cryptHeaders() ); +} +#endif + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: format() / compressionFormat()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + const auto testFormat = GENERATE( as< TestOutputFormat >(), + TestOutputFormat{ "ZIP", BitFormat::Zip }, + TestOutputFormat{ "BZIP2", BitFormat::BZip2 }, + TestOutputFormat{ "7Z", BitFormat::SevenZip }, + TestOutputFormat{ "XZ", BitFormat::Xz }, + TestOutputFormat{ "WIM", BitFormat::Wim }, + TestOutputFormat{ "TAR", BitFormat::Tar }, + TestOutputFormat{ "GZIP", BitFormat::GZip } ); + DYNAMIC_SECTION( "Format: " << testFormat.name ) { + const TestType compressor{ lib, testFormat.format }; + REQUIRE( compressor.compressionFormat() == testFormat.format ); + REQUIRE( compressor.format() == testFormat.format ); + } +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setCompressionLevel(...) / compressionLevel()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Normal ); + compressor.setCompressionLevel( BitCompressionLevel::None ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::None ); + compressor.setCompressionLevel( BitCompressionLevel::Fastest ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Fastest ); + compressor.setCompressionLevel( BitCompressionLevel::Fast ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Fast ); + compressor.setCompressionLevel( BitCompressionLevel::Normal ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Normal ); + compressor.setCompressionLevel( BitCompressionLevel::Max ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Max ); + compressor.setCompressionLevel( BitCompressionLevel::Ultra ); + REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Ultra ); +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setCompressionMethod(...) / compressionMethod()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + SECTION( "7Z Compression Methods" ) { + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); + compressor.setCompressionMethod( BitCompressionMethod::Copy ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); + compressor.setCompressionMethod( BitCompressionMethod::Lzma ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma ); + compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); + compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Ppmd ); + compressor.setCompressionMethod( BitCompressionMethod::BZip2 ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); + } + + SECTION( "ZIP Compression Methods" ) { + TestType compressor( lib, BitFormat::Zip ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); + compressor.setCompressionMethod( BitCompressionMethod::Copy ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); + compressor.setCompressionMethod( BitCompressionMethod::Deflate ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); + compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate64 ); + compressor.setCompressionMethod( BitCompressionMethod::BZip2 ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); + compressor.setCompressionMethod( BitCompressionMethod::Lzma ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma ); + compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Ppmd ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); + } + + SECTION( "BZIP2 Compression Methods" ) { + TestType compressor( lib, BitFormat::BZip2 ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); + } + + SECTION( "GZIP Compression Methods" ) { + TestType compressor( lib, BitFormat::GZip ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); + } + + SECTION( "TAR Compression Methods" ) { + TestType compressor( lib, BitFormat::Tar ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); + } + + SECTION( "WIM Compression Methods" ) { + TestType compressor( lib, BitFormat::Wim ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); + } + + SECTION( "XZ Compression Methods" ) { + TestType compressor( lib, BitFormat::Xz ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); + REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); + REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); + } +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setDictionarySize(...) / dictionarySize()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + SECTION( "SevenZip format + Lzma/Lzma2 compression methods" ) { + constexpr auto kMaxLzmaDictionarySize = 1536 * ( 1LL << 20 ); // less than 1536 MiB + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.dictionarySize() == 0 ); + + auto testMethod = GENERATE( BitCompressionMethod::Lzma, BitCompressionMethod::Lzma2 ); + compressor.setCompressionMethod( testMethod ); + + uint32_t dictionarySize = 1024 * 1024 * 1024; + compressor.setDictionarySize( dictionarySize ); + REQUIRE( compressor.dictionarySize() == dictionarySize ); + + compressor.setDictionarySize( kMaxLzmaDictionarySize ); + REQUIRE( compressor.dictionarySize() == kMaxLzmaDictionarySize ); + + dictionarySize = std::numeric_limits< uint32_t >::max(); + REQUIRE_THROWS( compressor.setDictionarySize( std::numeric_limits< uint32_t >::max() ) ); + REQUIRE( compressor.dictionarySize() == kMaxLzmaDictionarySize ); + } + + SECTION( "Zip format + Ppmd compression methods" ) { + constexpr uint32_t kMaxPpmdDictionarySize = ( 1ULL << 30 ); // less than 1 GiB, i.e., 2^30 bytes + + TestType compressor( lib, BitFormat::Zip ); + REQUIRE( compressor.dictionarySize() == 0 ); + compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); + + uint32_t dictionarySize = 1024 * 1024 * 1024; + compressor.setDictionarySize( dictionarySize ); + REQUIRE( compressor.dictionarySize() == dictionarySize ); + + compressor.setDictionarySize( kMaxPpmdDictionarySize ); + REQUIRE( compressor.dictionarySize() == kMaxPpmdDictionarySize ); + + dictionarySize = std::numeric_limits< uint32_t >::max(); + REQUIRE_THROWS( compressor.setDictionarySize( std::numeric_limits< uint32_t >::max() ) ); + REQUIRE( compressor.dictionarySize() == kMaxPpmdDictionarySize ); + + auto testMethod = GENERATE( BitCompressionMethod::Copy, + BitCompressionMethod::Deflate, + BitCompressionMethod::Deflate64 ); + compressor.setCompressionMethod( testMethod ); + REQUIRE_NOTHROW( compressor.setDictionarySize( 1024 * 1024 ) ); + REQUIRE( compressor.dictionarySize() == 0 ); + } + + SECTION( "BZip2 format and compression methods" ) { + constexpr auto kMaxBzip2DictionarySize = 900 * ( 1LL << 10 ); // less than 900 KiB + + TestType compressor( lib, BitFormat::BZip2 ); + REQUIRE( compressor.dictionarySize() == 0 ); + + uint32_t dictionarySize = 1024; + compressor.setDictionarySize( dictionarySize ); + REQUIRE( compressor.dictionarySize() == dictionarySize ); + + compressor.setDictionarySize( kMaxBzip2DictionarySize ); + REQUIRE( compressor.dictionarySize() == kMaxBzip2DictionarySize ); + + dictionarySize = std::numeric_limits< uint32_t >::max(); + REQUIRE_THROWS( compressor.setDictionarySize( std::numeric_limits< uint32_t >::max() ) ); + REQUIRE( compressor.dictionarySize() == kMaxBzip2DictionarySize ); + } +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setSolidMode(...) / solidMode()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( !compressor.solidMode() ); + + compressor.setSolidMode( true ); + REQUIRE( compressor.solidMode() ); + + compressor.setSolidMode( false ); + REQUIRE( !compressor.solidMode() ); +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setStoreSymbolicLinks(...) / storeSymbolicLinks()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE_FALSE( compressor.storeSymbolicLinks() ); + + compressor.setStoreSymbolicLinks( true ); + REQUIRE( compressor.storeSymbolicLinks() ); + REQUIRE( compressor.solidMode() ); + + compressor.setStoreSymbolicLinks( false ); + REQUIRE_FALSE( compressor.storeSymbolicLinks() ); + REQUIRE_FALSE( compressor.solidMode() ); +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setThreadCount(...) / threadCount()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.threadsCount() == 0u ); + compressor.setThreadsCount( 8u ); + REQUIRE( compressor.threadsCount() == 8u ); +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setUpdateMode(...) / updateMode()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.updateMode() == UpdateMode::None ); + + compressor.setUpdateMode( true ); + REQUIRE( compressor.updateMode() == UpdateMode::Append ); + + compressor.setUpdateMode( false ); + REQUIRE( compressor.updateMode() == UpdateMode::None ); + + compressor.setUpdateMode( UpdateMode::Append ); + REQUIRE( compressor.updateMode() == UpdateMode::Append ); + + compressor.setUpdateMode( UpdateMode::Update ); + REQUIRE( compressor.updateMode() == UpdateMode::Update ); + + compressor.setUpdateMode( UpdateMode::None ); + REQUIRE( compressor.updateMode() == UpdateMode::None ); + +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setVolumeSize(...) / volumeSize()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE( compressor.volumeSize() == 0u ); + compressor.setVolumeSize( 1024u ); + REQUIRE( compressor.volumeSize() == 1024u ); +} + +TEMPLATE_LIST_TEST_CASE( "BitAbstractArchiveCreator: setWordSize(...) / wordSize()", + "[bitabstractarchivecreator]", CreatorTypes ) { + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + + constexpr auto kMinPpmdWordSize = 2u; + + SECTION( "SevenZip format + Lzma/Lzma2 compression methods" ) { + constexpr auto kMinLzmaWordSize = 5u; + constexpr auto kMaxLzmaWordSize = 273u; + + TestType compressor( lib, BitFormat::SevenZip ); + REQUIRE_THROWS( compressor.setWordSize( 4u ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMinLzmaWordSize ) ); + REQUIRE( compressor.wordSize() == kMinLzmaWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 128u ) ); + REQUIRE( compressor.wordSize() == 128u ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMaxLzmaWordSize ) ); + REQUIRE( compressor.wordSize() == kMaxLzmaWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( kMaxLzmaWordSize + 1 ) ); + REQUIRE( compressor.wordSize() == kMaxLzmaWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( 512u ) ); + REQUIRE( compressor.wordSize() == kMaxLzmaWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 0 ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); + REQUIRE_NOTHROW( compressor.setWordSize( 64u ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); + REQUIRE_NOTHROW( compressor.setWordSize( 64u ) ); + REQUIRE( compressor.wordSize() == 0 ); + } + + SECTION( "SevenZip format + Ppmd compression method" ) { + constexpr auto kMax7zPpmdWordSize = 32u; + + TestType compressor( lib, BitFormat::SevenZip ); + compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); + + REQUIRE_THROWS( compressor.setWordSize( 1u ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMinPpmdWordSize ) ); + REQUIRE( compressor.wordSize() == kMinPpmdWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 16u ) ); + REQUIRE( compressor.wordSize() == 16u ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMax7zPpmdWordSize ) ); + REQUIRE( compressor.wordSize() == kMax7zPpmdWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( kMax7zPpmdWordSize + 1 ) ); + REQUIRE( compressor.wordSize() == kMax7zPpmdWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( 64u ) ); + REQUIRE( compressor.wordSize() == kMax7zPpmdWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 0 ) ); + REQUIRE( compressor.wordSize() == 0 ); + } + + SECTION( "Zip format + Ppmd compression method") { + constexpr auto kMaxZipPpmdWordSize = 16u; + + TestType compressor( lib, BitFormat::Zip ); + compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); + + REQUIRE_THROWS( compressor.setWordSize( 1u ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMinPpmdWordSize ) ); + REQUIRE( compressor.wordSize() == kMinPpmdWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 16u ) ); + REQUIRE( compressor.wordSize() == 16u ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMaxZipPpmdWordSize ) ); + REQUIRE( compressor.wordSize() == kMaxZipPpmdWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( kMaxZipPpmdWordSize + 1 ) ); + REQUIRE( compressor.wordSize() == kMaxZipPpmdWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( 32u ) ); + REQUIRE( compressor.wordSize() == kMaxZipPpmdWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 0 ) ); + REQUIRE( compressor.wordSize() == 0 ); + } + + SECTION( "Zip format + Deflate/Deflate64 compression method") { + constexpr auto kMinDeflateWordSize = 3u; + constexpr auto kMaxDeflateWordSize = 258u; + constexpr auto kMaxDeflate64WordSize = kMaxDeflateWordSize - 1; + + TestType compressor( lib, BitFormat::Zip ); + compressor.setCompressionMethod( BitCompressionMethod::Deflate ); + + REQUIRE_THROWS( compressor.setWordSize( 2u ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMinDeflateWordSize ) ); + REQUIRE( compressor.wordSize() == kMinDeflateWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 64u ) ); + REQUIRE( compressor.wordSize() == 64u ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMaxDeflateWordSize ) ); + REQUIRE( compressor.wordSize() == kMaxDeflateWordSize ); + + REQUIRE_THROWS( compressor.setWordSize( kMaxDeflateWordSize + 1 ) ); + REQUIRE( compressor.wordSize() == kMaxDeflateWordSize ); + + compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_THROWS( compressor.setWordSize( 1u ) ); + REQUIRE( compressor.wordSize() == 0 ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMinDeflateWordSize ) ); + REQUIRE( compressor.wordSize() == kMinDeflateWordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 64u ) ); + REQUIRE( compressor.wordSize() == 64u ); + + REQUIRE_NOTHROW( compressor.setWordSize( kMaxDeflate64WordSize ) ); + REQUIRE( compressor.wordSize() == kMaxDeflate64WordSize ); + + REQUIRE_THROWS( compressor.setWordSize( kMaxDeflate64WordSize + 1 ) ); + REQUIRE( compressor.wordSize() == kMaxDeflate64WordSize ); + + REQUIRE_NOTHROW( compressor.setWordSize( 0 ) ); + REQUIRE( compressor.wordSize() == 0 ); + } +} \ No newline at end of file diff --git a/tests/src/test_bitarchivereader.cpp b/tests/src/test_bitarchivereader.cpp index fc3478fe..c3fdbdb3 100644 --- a/tests/src/test_bitarchivereader.cpp +++ b/tests/src/test_bitarchivereader.cpp @@ -19,11 +19,11 @@ #include #include #include -#include +#include #include // Needed by MSVC for defining the S_XXXX macros. -#ifndef _CRT_INTERNAL_NONSTDC_NAMES // NOLINT(*-reserved-identifier) +#ifndef _CRT_INTERNAL_NONSTDC_NAMES // NOLINT(*-reserved-identifier, *-dcl37-c) #define _CRT_INTERNAL_NONSTDC_NAMES 1 #endif @@ -159,7 +159,7 @@ void getInputArchive( const fs::path& path, stream_t& archive ) { } template< typename T > -using is_filesystem_archive = std::is_same< bit7z::tstring, std::decay_t< T > >; +using is_filesystem_archive = std::is_same< bit7z::tstring, typename std::decay< T >::type >; TEMPLATE_TEST_CASE( "BitArchiveReader: Reading archives containing only a single file", "[bitarchivereader]", tstring, buffer_t, stream_t ) { @@ -469,6 +469,7 @@ TEMPLATE_TEST_CASE( "BitArchiveReader: Reading an empty archive", } else { REQUIRE( info.archivePath().empty() ); // No archive path for buffer/streamed archives } + REQUIRE_FALSE( info.isEncrypted() ); REQUIRE_ARCHIVE_CONTENT( info, testArchive ); REQUIRE_ARCHIVE_TESTS( info ); } diff --git a/tests/src/test_bitexception.cpp b/tests/src/test_bitexception.cpp index 480ea31e..7161fcc5 100644 --- a/tests/src/test_bitexception.cpp +++ b/tests/src/test_bitexception.cpp @@ -10,7 +10,7 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#if !defined(__GNUC__) || __GNUC__ >= 5 +#if !defined(__GNUC__) || __GNUC__ >= 5 || defined( __clang__ ) #include @@ -197,13 +197,14 @@ TEST_CASE( "BitException: Constructing from Win32/POSIX error codes", "[BitExcep const auto exception = BitException( "Hello World", sys_error ); #ifdef _WIN32 REQUIRE( exception.nativeCode() == HRESULT_FROM_WIN32( test.error ) ); + REQUIRE( exception.hresultCode() == exception.nativeCode() ); #else REQUIRE( exception.nativeCode() == test.error ); -#endif if ( sys_error != std::errc::io_error ) { // Multiple Win32 errors might be mapped to the POSIX IO error. REQUIRE( exception.hresultCode() == HRESULT_FROM_WIN32( test.error ) ); - REQUIRE( exception.posixCode() == sys_error.default_error_condition().value() ); } +#endif + REQUIRE( exception.posixCode() == sys_error.default_error_condition().value() ); REQUIRE( exception.what() == std::string{ "Hello World: " } + sys_error.message() ); } } @@ -253,7 +254,7 @@ TEST_CASE( "BitException: Checking if failed files are moved to the exception co REQUIRE( exceptionFailedFiles.size() == 1 ); REQUIRE( exceptionFailedFiles[ 0 ].first == BIT7Z_STRING( "hello.txt" ) ); REQUIRE( exceptionFailedFiles[ 0 ].second == std::errc::bad_file_descriptor ); - // Note: BitException should have cleared failedFiles, so it is again usable! + // Note: BitException should have cleared failedFiles, so it is again usable. REQUIRE( failedFiles.empty() ); // NOLINT(bugprone-use-after-move) //-V1030 } diff --git a/tests/src/test_bitfilecompressor.cpp b/tests/src/test_bitfilecompressor.cpp index 5b08d76d..24a21314 100644 --- a/tests/src/test_bitfilecompressor.cpp +++ b/tests/src/test_bitfilecompressor.cpp @@ -21,196 +21,7 @@ using bit7z::Bit7zLibrary; using bit7z::BitFileCompressor; using bit7z::BitInOutFormat; -struct TestOutputFormat { - const char* name; - const BitInOutFormat& format; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) -}; +TEST_CASE( "BitFileCompressor: TODO", "[bitfilecompressor]" ) { -TEST_CASE( "BitFileCompressor: Basic API tests (BitArchiveCreator API)", "[bitfilecompressor]" ) { - const Bit7zLibrary lib{ test::sevenzip_lib_path() }; - - SECTION( "setPassword(...) / password() / cryptHeaders()" ) { - BitFileCompressor compressor( lib, BitFormat::SevenZip ); - REQUIRE( compressor.password().empty() ); - REQUIRE( !compressor.cryptHeaders() ); - - compressor.setPassword( BIT7Z_STRING( "ciao" ) ); - REQUIRE( compressor.password() == BIT7Z_STRING( "ciao" ) ); - REQUIRE( !compressor.cryptHeaders() ); - - compressor.setPassword( BIT7Z_STRING( "mondo" ), true ); - REQUIRE( compressor.password() == BIT7Z_STRING( "mondo" ) ); - REQUIRE( compressor.cryptHeaders() ); - - compressor.setPassword( BIT7Z_STRING( "hello" ) ); - REQUIRE( compressor.password() == BIT7Z_STRING( "hello" ) ); - REQUIRE( compressor.cryptHeaders() ); - - compressor.setPassword( BIT7Z_STRING( "" ) ); - REQUIRE( compressor.password().empty() ); - REQUIRE( !compressor.cryptHeaders() ); - - BitAbstractArchiveHandler& handler = compressor; - handler.setPassword( BIT7Z_STRING( "world!" ) ); - REQUIRE( compressor.password() == BIT7Z_STRING( "world!" ) ); - REQUIRE( !compressor.cryptHeaders() ); - - compressor.setPassword( BIT7Z_STRING( "foo" ), true ); - handler.setPassword( BIT7Z_STRING( "" ) ); - REQUIRE( compressor.password().empty() ); - REQUIRE( !compressor.cryptHeaders() ); - } - - SECTION( "compressionFormat()" ) { - const auto testFormat = GENERATE( as< TestOutputFormat >(), - TestOutputFormat{ "ZIP", BitFormat::Zip }, - TestOutputFormat{ "BZIP2", BitFormat::BZip2 }, - TestOutputFormat{ "7Z", BitFormat::SevenZip }, - TestOutputFormat{ "XZ", BitFormat::Xz }, - TestOutputFormat{ "WIM", BitFormat::Wim }, - TestOutputFormat{ "TAR", BitFormat::Tar }, - TestOutputFormat{ "GZIP", BitFormat::GZip } ); - DYNAMIC_SECTION( "Format: " << testFormat.name ) { - const BitFileCompressor compressor{ lib, testFormat.format }; - REQUIRE( compressor.compressionFormat() == testFormat.format ); - } - } - - SECTION( "setCompressionLevel(...) / compressionLevel()" ) { - BitFileCompressor compressor( lib, BitFormat::SevenZip ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Normal ); - compressor.setCompressionLevel( BitCompressionLevel::None ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::None ); - compressor.setCompressionLevel( BitCompressionLevel::Fastest ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Fastest ); - compressor.setCompressionLevel( BitCompressionLevel::Fast ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Fast ); - compressor.setCompressionLevel( BitCompressionLevel::Normal ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Normal ); - compressor.setCompressionLevel( BitCompressionLevel::Max ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Max ); - compressor.setCompressionLevel( BitCompressionLevel::Ultra ); - REQUIRE( compressor.compressionLevel() == BitCompressionLevel::Ultra ); - } - - SECTION( "setCompressionMethod(...) / compressionMethod()" ) { - SECTION( "7Z Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::SevenZip ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); - compressor.setCompressionMethod( BitCompressionMethod::Copy ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); - compressor.setCompressionMethod( BitCompressionMethod::Lzma ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma ); - compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); - compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Ppmd ); - compressor.setCompressionMethod( BitCompressionMethod::BZip2 ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); - } - - SECTION( "ZIP Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::Zip ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); - compressor.setCompressionMethod( BitCompressionMethod::Copy ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); - compressor.setCompressionMethod( BitCompressionMethod::Deflate ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); - compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate64 ); - compressor.setCompressionMethod( BitCompressionMethod::BZip2 ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); - compressor.setCompressionMethod( BitCompressionMethod::Lzma ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma ); - compressor.setCompressionMethod( BitCompressionMethod::Ppmd ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Ppmd ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); - } - - SECTION( "BZIP2 Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::BZip2 ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); - REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::BZip2 ); - } - - SECTION( "GZIP Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::GZip ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); - REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Deflate ); - } - - SECTION( "TAR Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::Tar ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); - REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); - } - - SECTION( "WIM Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::Wim ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); - REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Copy ); - } - - SECTION( "XZ Compression Methods" ) { - BitFileCompressor compressor( lib, BitFormat::Xz ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Copy ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Lzma ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Ppmd ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::BZip2 ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate ) ); - REQUIRE_THROWS( compressor.setCompressionMethod( BitCompressionMethod::Deflate64 ) ); - REQUIRE_NOTHROW( compressor.setCompressionMethod( BitCompressionMethod::Lzma2 ) ); - REQUIRE( compressor.compressionMethod() == BitCompressionMethod::Lzma2 ); - } - } - - SECTION( "setSolidMode(...) / solidMode()" ) { - BitFileCompressor compressor( lib, BitFormat::SevenZip ); - REQUIRE( !compressor.solidMode() ); - - compressor.setSolidMode( true ); - REQUIRE( compressor.solidMode() ); - - compressor.setSolidMode( false ); - REQUIRE( !compressor.solidMode() ); - } - - SECTION( "setVolumeSize(...) / volumeSize()" ) { - BitFileCompressor compressor( lib, BitFormat::SevenZip ); - REQUIRE( compressor.volumeSize() == 0u ); - compressor.setVolumeSize( 1024u ); - REQUIRE( compressor.volumeSize() == 1024u ); - } } diff --git a/tests/src/test_bititemsvector.cpp b/tests/src/test_bititemsvector.cpp index 2bb632bb..bf5e0a67 100644 --- a/tests/src/test_bititemsvector.cpp +++ b/tests/src/test_bititemsvector.cpp @@ -1973,7 +1973,7 @@ TEST_CASE( "BitItemsVector: Indexing a single buffer", "[bititemsvector]" ) { BIT7Z_STRING( "folder/subfolder2/frequency.xlsx" ), BIT7Z_STRING( "dot.folder/hello.json" ) ); - DYNAMIC_SECTION ( "Indexing file " << testInput.u8string() << " as a buffer" ) { + DYNAMIC_SECTION( "Indexing file " << testInput.u8string() << " as a buffer" ) { REQUIRE_LOAD_FILE( input_buffer, testInput ); REQUIRE_NOTHROW( itemsVector.indexBuffer( input_buffer, BIT7Z_STRING( "custom_name.ext" ) ) ); REQUIRE( itemsVector.size() == 1 ); diff --git a/tests/src/test_formatdetect.cpp b/tests/src/test_formatdetect.cpp index 7ce1a3a3..4e59009e 100644 --- a/tests/src/test_formatdetect.cpp +++ b/tests/src/test_formatdetect.cpp @@ -28,7 +28,7 @@ using namespace bit7z; using namespace bit7z::test; using namespace bit7z::test::filesystem; -// Note: format detection by extension doesn't actually require the file to exist! +// Note: format detection by extension doesn't actually require the file to exist. TEST_CASE( "formatdetect: Format detection by extension", "[formatdetect]" ) { const TestDirectory testDir{ fs::path{ test_archives_dir } / "detection" / "valid" }; @@ -265,19 +265,25 @@ TEST_CASE( "formatdetect: Format detection of archive with a wrong extension (Is const Bit7zLibrary lib{ test::sevenzip_lib_path() }; - // From file - REQUIRE_THROWS( BitArchiveReader( lib, BIT7Z_STRING( "wrong_extension.rar" ), BitFormat::Rar ) ); - REQUIRE_NOTHROW( BitArchiveReader( lib, BIT7Z_STRING( "wrong_extension.rar" ), BitFormat::SevenZip ) ); - REQUIRE_NOTHROW( BitArchiveReader( lib, BIT7Z_STRING( "wrong_extension.rar" ) ) ); - - // From buffer - auto fileBuffer = load_file( "wrong_extension.rar" ); - REQUIRE_THROWS( BitArchiveReader( lib, fileBuffer, BitFormat::Rar ) ); - REQUIRE_NOTHROW( BitArchiveReader( lib, fileBuffer, BitFormat::SevenZip ) ); - REQUIRE_NOTHROW( BitArchiveReader( lib, fileBuffer ) ); + auto testFile = GENERATE( as< tstring >(), + BIT7Z_STRING( "wrong_extension.rar" ), + BIT7Z_STRING( "wrong_extension.bz2" ) ); + + DYNAMIC_SECTION( "Reading file with a wrong extension: " << Catch::StringMaker< tstring >::convert( testFile ) ) { + // From file + REQUIRE_THROWS( BitArchiveReader( lib, testFile, BitFormat::Rar ) ); + REQUIRE_NOTHROW( BitArchiveReader( lib, testFile, BitFormat::SevenZip ) ); + REQUIRE_NOTHROW( BitArchiveReader( lib, testFile ) ); + + // From buffer + auto fileBuffer = load_file( testFile ); + REQUIRE_THROWS( BitArchiveReader( lib, fileBuffer, BitFormat::Rar ) ); + REQUIRE_NOTHROW( BitArchiveReader( lib, fileBuffer, BitFormat::SevenZip ) ); + REQUIRE_NOTHROW( BitArchiveReader( lib, fileBuffer ) ); + } } -TEST_CASE( "BitArchiveReader: Format detection of an archive file without extension", "[bitarchivereader]" ) { +TEST_CASE( "formatdetect: Format detection of an archive file without an extension", "[formatdetect]" ) { const TestDirectory testDir{ fs::path{ test_archives_dir } / "detection" }; REQUIRE( detect_format_from_extension( "noextension" ) == BitFormat::Auto ); @@ -288,4 +294,4 @@ TEST_CASE( "BitArchiveReader: Format detection of an archive file without extens REQUIRE_NOTHROW( reader.test() ); } -#endif \ No newline at end of file +#endif // BIT7Z_AUTO_FORMAT \ No newline at end of file diff --git a/tests/src/test_stringutil.cpp b/tests/src/test_stringutil.cpp new file mode 100644 index 00000000..ff51e9e9 --- /dev/null +++ b/tests/src/test_stringutil.cpp @@ -0,0 +1,92 @@ +// This is an open source non-commercial project. Dear PVS-Studio, please check it. +// PVS-Studio Static Code Analyzer for C, C++ and C#: http://www.viva64.com + +/* + * bit7z - A C++ static library to interface with the 7-zip shared libraries. + * Copyright (c) 2014-2023 Riccardo Ostani - All Rights Reserved. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at https://mozilla.org/MPL/2.0/. + */ +#include + +#include + +#if !defined( _WIN32 ) || ( !defined( BIT7Z_USE_NATIVE_STRING ) && !defined( BIT7Z_USE_SYSTEM_CODEPAGE ) ) + +#define NARROWING_TEST_STR( str ) std::make_tuple( L##str, (str) ) + +TEST_CASE( "util: Narrowing wide string to std::string", "[stringutil][narrow]" ) { + using bit7z::narrow; + + SECTION( "Converting from nullptr C wide string" ) { + REQUIRE( narrow( nullptr, 0 ).empty() ); + REQUIRE( narrow( nullptr, 42 ).empty() ); + } + +#if !defined( _LIBCPP_VERSION ) + SECTION( "Converting wide strings with unencodable UTF-8 chars" ) { + std::wstring testInput = L"\xDC80"; + std::string testOutput = narrow( testInput.c_str(), testInput.size() ); +#if defined( _MSC_VER ) || !defined( BIT7Z_USE_STANDARD_FILESYSTEM ) + REQUIRE( testOutput == "\uFFFD" ); +#else + REQUIRE( testOutput == "\xED\xB2\x80" ); +#endif + + testInput = L"\xD843"; + testOutput = narrow( testInput.c_str(), testInput.size() ); +#if defined( _MSC_VER ) || !defined( BIT7Z_USE_STANDARD_FILESYSTEM ) + REQUIRE( testOutput == "\uFFFD" ); +#else + REQUIRE( testOutput == "\xED\xA1\x83" ); +#endif + } +#endif + + SECTION( "Converting wide strings without unencodable UTF-8 characters" ) { + std::wstring testInput; + std::string testOutput; + std::tie( testInput, testOutput ) = GENERATE( table< const wchar_t*, const char* >( + { + NARROWING_TEST_STR( "" ), + NARROWING_TEST_STR( "h" ), + NARROWING_TEST_STR( "hello world!" ), + NARROWING_TEST_STR( "supercalifragilistichespiralidoso" ), + NARROWING_TEST_STR( "perché" ), + NARROWING_TEST_STR( "\u30e1\u30bf\u30eb\u30ac\u30eb\u30eb\u30e2\u30f3" ) // メタルガルルモン + } + ) ); + + DYNAMIC_SECTION( "Converting L\"" << testOutput << "\" to narrow string" ) { + REQUIRE( narrow( testInput.c_str(), testInput.size() ) == testOutput ); + } + } +} + +#define WIDENING_TEST_STR( str ) std::make_tuple( (str), L##str ) + +TEST_CASE( "util: Widening narrow string to std::wstring", "[stringutil][widen]" ) { + using bit7z::widen; + using std::make_tuple; + + std::string testInput; + std::wstring testOutput; + std::tie( testInput, testOutput ) = GENERATE( table< const char*, const wchar_t* >( + { + WIDENING_TEST_STR( "" ), + WIDENING_TEST_STR( "h" ), + WIDENING_TEST_STR( "hello world!" ), + WIDENING_TEST_STR( "supercalifragilistichespiralidoso" ), + WIDENING_TEST_STR( "perché" ), + WIDENING_TEST_STR( "\u30e1\u30bf\u30eb\u30ac\u30eb\u30eb\u30e2\u30f3" ) // メタルガルルモン + } + ) ); + + DYNAMIC_SECTION( "Converting \"" << testInput << "\" to wide string" ) { + REQUIRE( widen( testInput ) == testOutput ); + } +} + +#endif \ No newline at end of file diff --git a/tests/src/test_util.cpp b/tests/src/test_util.cpp index 7bd44878..8707d67c 100644 --- a/tests/src/test_util.cpp +++ b/tests/src/test_util.cpp @@ -14,88 +14,15 @@ #include -#if !defined( _WIN32 ) || ( !defined( BIT7Z_USE_NATIVE_STRING ) && !defined( BIT7Z_USE_SYSTEM_CODEPAGE ) ) - -#define NARROWING_TEST_STR( str ) std::make_tuple( L##str, (str) ) - -TEST_CASE( "util: Narrowing wide string to std::string", "[util][narrow]" ) { - using bit7z::narrow; - - SECTION( "Converting from nullptr C wide string" ) { - REQUIRE( narrow( nullptr, 0 ).empty() ); - REQUIRE( narrow( nullptr, 42 ).empty() ); - } - -#if !defined( _LIBCPP_VERSION ) - SECTION( "Converting wide strings with unencodable UTF-8 chars" ) { - std::wstring testInput = L"\xDC80"; - std::string testOutput = narrow( testInput.c_str(), testInput.size() ); -#if defined( _MSC_VER ) || !defined( BIT7Z_USE_STANDARD_FILESYSTEM ) - REQUIRE( testOutput == "\uFFFD" ); -#else - REQUIRE( testOutput == "\xED\xB2\x80" ); -#endif - - testInput = L"\xD843"; - testOutput = narrow( testInput.c_str(), testInput.size() ); -#if defined( _MSC_VER ) || !defined( BIT7Z_USE_STANDARD_FILESYSTEM ) - REQUIRE( testOutput == "\uFFFD" ); -#else - REQUIRE( testOutput == "\xED\xA1\x83" ); -#endif - } -#endif - - SECTION( "Converting wide strings without unencodable UTF-8 characters" ) { - std::wstring testInput; - std::string testOutput; - std::tie( testInput, testOutput ) = GENERATE( table< const wchar_t*, const char* >( - { - NARROWING_TEST_STR( "" ), - NARROWING_TEST_STR( "h" ), - NARROWING_TEST_STR( "hello world!" ), - NARROWING_TEST_STR( "supercalifragilistichespiralidoso" ), - NARROWING_TEST_STR( "perché" ), - NARROWING_TEST_STR( "\u30e1\u30bf\u30eb\u30ac\u30eb\u30eb\u30e2\u30f3" ) // メタルガルルモン - } - ) ); - - DYNAMIC_SECTION( "Converting L\"" << testOutput << "\" to narrow string" ) { - REQUIRE( narrow( testInput.c_str(), testInput.size() ) == testOutput ); - } - } -} - -#define WIDENING_TEST_STR( str ) std::make_tuple( (str), L##str ) - -TEST_CASE( "util: Widening narrow string to std::wstring", "[util][widen]" ) { - using bit7z::widen; - using std::make_tuple; - - std::string testInput; - std::wstring testOutput; - std::tie( testInput, testOutput ) = GENERATE( table< const char*, const wchar_t* >( - { - WIDENING_TEST_STR( "" ), - WIDENING_TEST_STR( "h" ), - WIDENING_TEST_STR( "hello world!" ), - WIDENING_TEST_STR( "supercalifragilistichespiralidoso" ), - WIDENING_TEST_STR( "perché" ), - WIDENING_TEST_STR( "\u30e1\u30bf\u30eb\u30ac\u30eb\u30eb\u30e2\u30f3" ) // メタルガルルモン - } - ) ); - - DYNAMIC_SECTION( "Converting \"" << testInput << "\" to wide string" ) { - REQUIRE( widen( testInput ) == testOutput ); - } -} -#endif - using bit7z::check_overflow; -constexpr auto kMaxValue = ( std::numeric_limits< int64_t >::max )(); -constexpr auto kMinValue = ( std::numeric_limits< int64_t >::min )(); +using bit7z::clamp_cast; +using bit7z::cmp_greater_equal; +using bit7z::seek_to_offset; TEST_CASE( "util: Calling check_overflow on a non-overflowing offset", "[util][check_overflow]" ) { //-V2008 + constexpr auto kMaxValue = ( std::numeric_limits< int64_t >::max )(); + constexpr auto kMinValue = ( std::numeric_limits< int64_t >::min )(); + REQUIRE_FALSE( check_overflow( kMaxValue, 0 ) ); REQUIRE_FALSE( check_overflow( kMaxValue, -1 ) ); REQUIRE_FALSE( check_overflow( kMaxValue, -42 ) ); @@ -144,6 +71,9 @@ TEST_CASE( "util: Calling check_overflow on a non-overflowing offset", "[util][c } TEST_CASE( "util: Calling check_overflow on an overflowing offset", "[util][check_overflow]" ) { + constexpr auto kMaxValue = ( std::numeric_limits< int64_t >::max )(); + constexpr auto kMinValue = ( std::numeric_limits< int64_t >::min )(); + REQUIRE( check_overflow( kMaxValue, kMaxValue ) ); REQUIRE( check_overflow( kMaxValue, 42 ) ); REQUIRE( check_overflow( kMaxValue, 1 ) ); @@ -154,4 +84,371 @@ TEST_CASE( "util: Calling check_overflow on an overflowing offset", "[util][chec REQUIRE( check_overflow( kMinValue, -1 ) ); REQUIRE( check_overflow( kMinValue, -42 ) ); REQUIRE( check_overflow( kMinValue, kMinValue ) ); +} + + +TEST_CASE( "util: Calculate the absolute position from the given position, and the offset", "[util][seek_to_offset]" ) { + uint64_t position = 0; + REQUIRE( seek_to_offset( position, 0 ) == S_OK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, 1 ) == S_OK ); + REQUIRE( position == 1 ); + + REQUIRE( seek_to_offset( position, -1 ) == S_OK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, -1 ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, 42 ) == S_OK ); + REQUIRE( position == 42 ); + + REQUIRE( seek_to_offset( position, -42 ) == S_OK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, -42 ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() + 1 ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == 0 ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::max() ) == S_OK ); + REQUIRE( position == static_cast< std::uint64_t >( std::numeric_limits< int64_t >::max() ) ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() + 1 ) == S_OK ); + REQUIRE( position == 0 ); + + position = 1; + REQUIRE( seek_to_offset( position, -42 ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == 1 ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == 1 ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::max() ) == S_OK ); + REQUIRE( position == ( 1u + static_cast< std::uint64_t >( std::numeric_limits< int64_t >::max() ) ) ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() ) == HRESULT_WIN32_ERROR_NEGATIVE_SEEK ); + REQUIRE( position == ( 1u + static_cast< std::uint64_t >( std::numeric_limits< int64_t >::max() ) ) ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() + 1 ) == S_OK ); + REQUIRE( position == 1 ); + + position = 64; + REQUIRE( seek_to_offset( position, -1 ) == S_OK ); + REQUIRE( position == 63 ); + + position = 64; + REQUIRE( seek_to_offset( position, 0 ) == S_OK ); + REQUIRE( position == 64 ); + + REQUIRE( seek_to_offset( position, 1 ) == S_OK ); + REQUIRE( position == 65 ); + + position = std::numeric_limits< uint64_t >::max() - 1u; + REQUIRE( seek_to_offset( position, 1 ) == S_OK ); + REQUIRE( position == std::numeric_limits< uint64_t >::max() ); + + REQUIRE( seek_to_offset( position, 1 ) == E_INVALIDARG ); + REQUIRE( position == std::numeric_limits< uint64_t >::max() ); + + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::min() + 1 ) == S_OK ); + REQUIRE( position == ( std::numeric_limits< uint64_t >::max() - static_cast< uint64_t >( std::numeric_limits< int64_t >::max() ) ) ); + + position = std::numeric_limits< uint64_t >::max(); + REQUIRE( seek_to_offset( position, std::numeric_limits< int64_t >::max() ) == E_INVALIDARG ); + REQUIRE( position == std::numeric_limits< uint64_t >::max() ); +} + +/* unsigned -> unsigned */ +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::uint8_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::uint8_t >( TestType{ 0u } ) == std::uint8_t{ 0u } ); + REQUIRE( clamp_cast< std::uint8_t >( TestType{ 1u } ) == std::uint8_t{ 1u } ); + REQUIRE( clamp_cast< std::uint8_t >( TestType{ 42u } ) == std::uint8_t{ 42u } ); + + if ( sizeof( TestType ) > sizeof( std::uint8_t ) ) { + TestType maxValue8bit{ std::numeric_limits< std::uint8_t >::max() }; + REQUIRE( clamp_cast< std::uint8_t >( maxValue8bit ) == std::numeric_limits< std::uint8_t >::max() ); + } + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + REQUIRE( clamp_cast< std::uint8_t >( maxValue ) == std::numeric_limits< std::uint8_t >::max() ); +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::uint16_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::uint16_t >( TestType{ 0u } ) == std::uint16_t{ 0u } ); + REQUIRE( clamp_cast< std::uint16_t >( TestType{ 1u } ) == std::uint16_t{ 1u } ); + REQUIRE( clamp_cast< std::uint16_t >( TestType{ 42u } ) == std::uint16_t{ 42u } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::uint16_t maxDestinationValue{ std::numeric_limits< std::uint16_t >::max() }; + if ( maxValue >= maxDestinationValue ) { + REQUIRE( clamp_cast< std::uint16_t >( maxValue ) == maxDestinationValue ); + REQUIRE( clamp_cast< std::uint16_t >( static_cast< TestType >( maxDestinationValue ) ) == maxDestinationValue ); + } else { // widening + REQUIRE( clamp_cast< std::uint16_t >( maxValue ) == maxValue ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::uint32_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::uint32_t >( TestType{ 0u } ) == std::uint32_t{ 0u } ); + REQUIRE( clamp_cast< std::uint32_t >( TestType{ 1u } ) == std::uint32_t{ 1u } ); + REQUIRE( clamp_cast< std::uint32_t >( TestType{ 42u } ) == std::uint32_t{ 42u } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::uint32_t maxDestinationValue{ std::numeric_limits< std::uint32_t >::max() }; + if ( maxValue >= maxDestinationValue ) { + REQUIRE( clamp_cast< std::uint32_t >( maxValue ) == maxDestinationValue ); + REQUIRE( clamp_cast< std::uint32_t >( static_cast< TestType >( maxDestinationValue ) ) == maxDestinationValue ); + } else { // widening + REQUIRE( clamp_cast< std::uint32_t >( maxValue ) == maxValue ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from std::uint64_t to std::uint64_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::uint64_t >( std::uint64_t{ 0u } ) == std::uint64_t{ 0u } ); + REQUIRE( clamp_cast< std::uint64_t >( std::uint64_t{ 1u } ) == std::uint64_t{ 1u } ); + REQUIRE( clamp_cast< std::uint64_t >( std::uint64_t{ 42u } ) == std::uint64_t{ 42u } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + REQUIRE( clamp_cast< std::uint64_t >( maxValue ) == maxValue ); +} + +/* signed -> unsigned */ +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::uint8_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::uint8_t >( TestType{ 0 } ) == std::uint8_t{ 0u } ); + REQUIRE( clamp_cast< std::uint8_t >( TestType{ 1 } ) == std::uint8_t{ 1u } ); + REQUIRE( clamp_cast< std::uint8_t >( TestType{ 42 } ) == std::uint8_t{ 42u } ); + + REQUIRE( clamp_cast< std::uint8_t >( TestType{ -1 } ) == std::uint8_t{ 0u } ); + REQUIRE( clamp_cast< std::uint8_t >( TestType{ -42 } ) == std::uint8_t{ 0u } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + REQUIRE( clamp_cast< std::uint8_t >( minValue ) == 0u ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::uint8_t maxDestinationValue{ std::numeric_limits< std::uint8_t >::max() }; + if ( cmp_greater_equal( maxValue, maxDestinationValue ) ) { + REQUIRE( clamp_cast< std::uint8_t >( maxValue ) == maxDestinationValue ); + } else { + REQUIRE( clamp_cast< std::uint8_t >( maxValue ) == static_cast< std::uint8_t >( maxValue ) ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::uint16_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::uint16_t >( TestType{ 0 } ) == std::uint16_t{ 0u } ); + REQUIRE( clamp_cast< std::uint16_t >( TestType{ 1 } ) == std::uint16_t{ 1u } ); + REQUIRE( clamp_cast< std::uint16_t >( TestType{ 42 } ) == std::uint16_t{ 42u } ); + + REQUIRE( clamp_cast< std::uint16_t >( TestType{ -1 } ) == std::uint16_t{ 0u } ); + REQUIRE( clamp_cast< std::uint16_t >( TestType{ -42 } ) == std::uint16_t{ 0u } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + REQUIRE( clamp_cast< std::uint16_t >( minValue ) == 0u ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::uint16_t maxDestinationValue{ std::numeric_limits< std::uint16_t >::max() }; + if ( cmp_greater_equal( maxValue, maxDestinationValue ) ) { + REQUIRE( clamp_cast< std::uint16_t >( maxValue ) == maxDestinationValue ); + } else { + REQUIRE( clamp_cast< std::uint16_t >( maxValue ) == static_cast< std::uint16_t >( maxValue ) ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::uint32_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::uint32_t >( TestType{ 0 } ) == std::uint32_t{ 0u } ); + REQUIRE( clamp_cast< std::uint32_t >( TestType{ 1 } ) == std::uint32_t{ 1u } ); + REQUIRE( clamp_cast< std::uint32_t >( TestType{ 42 } ) == std::uint32_t{ 42u } ); + + REQUIRE( clamp_cast< std::uint32_t >( TestType{ -1 } ) == std::uint32_t{ 0u } ); + REQUIRE( clamp_cast< std::uint32_t >( TestType{ -42 } ) == std::uint32_t{ 0u } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + REQUIRE( clamp_cast< std::uint32_t >( minValue ) == 0u ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::uint32_t maxDestinationValue{ std::numeric_limits< std::uint32_t >::max() }; + if ( cmp_greater_equal( maxValue, maxDestinationValue ) ) { + REQUIRE( clamp_cast< std::uint32_t >( maxValue ) == maxDestinationValue ); + } else { + REQUIRE( clamp_cast< std::uint32_t >( maxValue ) == static_cast< std::uint32_t >( maxValue ) ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::uint64_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::uint64_t >( TestType{ 0 } ) == std::uint64_t{ 0u } ); + REQUIRE( clamp_cast< std::uint64_t >( TestType{ 1 } ) == std::uint64_t{ 1u } ); + REQUIRE( clamp_cast< std::uint64_t >( TestType{ 42 } ) == std::uint64_t{ 42u } ); + + REQUIRE( clamp_cast< std::uint64_t >( TestType{ -1 } ) == std::uint64_t{ 0u } ); + REQUIRE( clamp_cast< std::uint64_t >( TestType{ -42 } ) == std::uint64_t{ 0u } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + REQUIRE( clamp_cast< std::uint64_t >( minValue ) == 0u ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + REQUIRE( clamp_cast< std::uint64_t >( maxValue ) == static_cast< std::uint64_t >( maxValue ) ); +} + +/* unsigned -> signed */ +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::int8_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::int8_t >( TestType{ 0u } ) == std::int8_t{ 0 } ); + REQUIRE( clamp_cast< std::int8_t >( TestType{ 1u } ) == std::int8_t{ 1 } ); + REQUIRE( clamp_cast< std::int8_t >( TestType{ 42u } ) == std::int8_t{ 42 } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + REQUIRE( clamp_cast< std::int8_t >( maxValue ) == std::numeric_limits< std::int8_t >::max() ); +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::int16_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::int16_t >( TestType{ 0u } ) == std::int16_t{ 0 } ); + REQUIRE( clamp_cast< std::int16_t >( TestType{ 1u } ) == std::int16_t{ 1 } ); + REQUIRE( clamp_cast< std::int16_t >( TestType{ 42u } ) == std::int16_t{ 42 } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::int16_t maxDestinationValue{ std::numeric_limits< std::int16_t >::max() }; + if ( cmp_greater_equal( maxValue, maxDestinationValue ) ) { + REQUIRE( clamp_cast< std::int16_t >( maxValue ) == maxDestinationValue ); + } else { // widening + REQUIRE( clamp_cast< std::int16_t >( maxValue ) == maxValue ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::int32_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::int32_t >( TestType{ 0u } ) == std::int32_t{ 0 } ); + REQUIRE( clamp_cast< std::int32_t >( TestType{ 1u } ) == std::int32_t{ 1 } ); + REQUIRE( clamp_cast< std::int32_t >( TestType{ 42u } ) == std::int32_t{ 42 } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::int32_t maxDestinationValue{ std::numeric_limits< std::int32_t >::max() }; + if ( cmp_greater_equal( maxValue, maxDestinationValue ) ) { + REQUIRE( clamp_cast< std::int32_t >( maxValue ) == maxDestinationValue ); + } else { // widening + REQUIRE( clamp_cast< std::int32_t >( maxValue ) == maxValue ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any unsigned type to std::int64_t", "[util][clamp_cast]", + std::uint8_t, std::uint16_t, std::uint32_t, std::uint64_t ) { + REQUIRE( clamp_cast< std::int64_t >( TestType{ 0u } ) == std::int64_t{ 0 } ); + REQUIRE( clamp_cast< std::int64_t >( TestType{ 1u } ) == std::int64_t{ 1 } ); + REQUIRE( clamp_cast< std::int64_t >( TestType{ 42u } ) == std::int64_t{ 42 } ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::int64_t maxDestinationValue{ std::numeric_limits< std::int64_t >::max() }; + if ( cmp_greater_equal( maxValue, maxDestinationValue ) ) { + REQUIRE( clamp_cast< std::int64_t >( maxValue ) == maxDestinationValue ); + } else { // widening + REQUIRE( clamp_cast< std::int64_t >( maxValue ) == maxValue ); + } +} + +/* signed -> signed */ +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::int8_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::int8_t >( TestType{ 0 } ) == std::int8_t{ 0 } ); + REQUIRE( clamp_cast< std::int8_t >( TestType{ 1 } ) == std::int8_t{ 1 } ); + REQUIRE( clamp_cast< std::int8_t >( TestType{ 42 } ) == std::int8_t{ 42 } ); + + REQUIRE( clamp_cast< std::int8_t >( TestType{ -1 } ) == std::int8_t{ -1 } ); + REQUIRE( clamp_cast< std::int8_t >( TestType{ -42 } ) == std::int8_t{ -42 } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + REQUIRE( clamp_cast< std::int8_t >( minValue ) == std::numeric_limits< std::int8_t >::min() ); + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + REQUIRE( clamp_cast< std::int8_t >( maxValue ) == std::numeric_limits< std::int8_t >::max() ); +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::int16_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::int16_t >( TestType{ 0 } ) == std::int16_t{ 0 } ); + REQUIRE( clamp_cast< std::int16_t >( TestType{ 1 } ) == std::int16_t{ 1 } ); + REQUIRE( clamp_cast< std::int16_t >( TestType{ 42 } ) == std::int16_t{ 42 } ); + + REQUIRE( clamp_cast< std::int16_t >( TestType{ -1 } ) == std::int16_t{ -1 } ); + REQUIRE( clamp_cast< std::int16_t >( TestType{ -42 } ) == std::int16_t{ -42 } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + std::int16_t minDestinationValue{ std::numeric_limits< std::int16_t >::min() }; + if ( minValue <= minDestinationValue ) { + REQUIRE( clamp_cast< std::int16_t >( minValue ) == minDestinationValue ); + } else { + REQUIRE( clamp_cast< std::int16_t >( minValue ) == minValue ); + } + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::int16_t maxDestinationValue{ std::numeric_limits< std::int16_t >::max() }; + if ( maxValue >= maxDestinationValue ) { + REQUIRE( clamp_cast< std::int16_t >( maxValue ) == maxDestinationValue ); + } else { + REQUIRE( clamp_cast< std::int16_t >( maxValue ) == maxValue ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from any signed type to std::int32_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::int32_t >( TestType{ 0 } ) == std::int32_t{ 0 } ); + REQUIRE( clamp_cast< std::int32_t >( TestType{ 1 } ) == std::int32_t{ 1 } ); + REQUIRE( clamp_cast< std::int32_t >( TestType{ 42 } ) == std::int32_t{ 42 } ); + + REQUIRE( clamp_cast< std::int32_t >( TestType{ -1 } ) == std::int32_t{ -1 } ); + REQUIRE( clamp_cast< std::int32_t >( TestType{ -42 } ) == std::int32_t{ -42 } ); + + TestType minValue{ std::numeric_limits< TestType >::min() }; + std::int32_t minDestinationValue{ std::numeric_limits< std::int32_t >::min() }; + if ( minValue <= minDestinationValue ) { + REQUIRE( clamp_cast< std::int32_t >( minValue ) == minDestinationValue ); + } else { + REQUIRE( clamp_cast< std::int32_t >( minValue ) == minValue ); + } + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::int32_t maxDestinationValue{ std::numeric_limits< std::int32_t >::max() }; + if ( maxValue >= maxDestinationValue ) { + REQUIRE( clamp_cast< std::int32_t >( maxValue ) == maxDestinationValue ); + } else { + REQUIRE( clamp_cast< std::int32_t >( maxValue ) == maxValue ); + } +} + +TEMPLATE_TEST_CASE( "util: Clamp cast from std::int64_t to std::int64_t", "[util][clamp_cast]", + std::int8_t, std::int16_t, std::int32_t, std::int64_t ) { + REQUIRE( clamp_cast< std::int64_t >( TestType{ 0 } ) == std::int64_t{ 0 } ); + REQUIRE( clamp_cast< std::int64_t >( TestType{ 1 } ) == std::int64_t{ 1 } ); + REQUIRE( clamp_cast< std::int64_t >( TestType{ 42 } ) == std::int64_t{ 42 } ); + + REQUIRE( clamp_cast< std::int64_t >( TestType{ -1 } ) == std::int64_t{ -1 } ); + REQUIRE( clamp_cast< std::int64_t >( TestType{ -42 } ) == std::int64_t{ -42 } ); + + TestType minValue{ std::numeric_limits< TestType >::max() }; + std::int64_t minDestinationValue{ std::numeric_limits< std::int64_t >::min() }; + if ( minValue <= minDestinationValue ) { + REQUIRE( clamp_cast< std::int64_t >( minValue ) == minDestinationValue ); + } else { + REQUIRE( clamp_cast< std::int64_t >( minValue ) == minValue ); + } + + TestType maxValue{ std::numeric_limits< TestType >::max() }; + std::int64_t maxDestinationValue{ std::numeric_limits< std::int64_t >::max() }; + if ( maxValue >= maxDestinationValue ) { + REQUIRE( clamp_cast< std::int64_t >( maxValue ) == maxDestinationValue ); + } else { + REQUIRE( clamp_cast< std::int64_t >( maxValue ) == maxValue ); + } } \ No newline at end of file diff --git a/tests/src/test_windows.cpp b/tests/src/test_windows.cpp index fdf011c6..9903d199 100644 --- a/tests/src/test_windows.cpp +++ b/tests/src/test_windows.cpp @@ -112,7 +112,7 @@ TEST_CASE( "winapi: Allocating from wide strings", "[winapi][string allocation]" SECTION( "SysAllocStringLen with half-length parameter" ) { // Note: flawfinder warns about potentially using non-null terminating strings, - // but, in our case, the test string is guaranteed to be null-terminated! + // but, in our case, the test string is guaranteed to be null-terminated. expectedString = std::wstring{ testStr, std::wcslen( testStr ) / 2 }; // flawfinder: ignore resultString = SysAllocStringLen( testStr, expectedString.size() ); } @@ -152,7 +152,7 @@ TEST_CASE( "winapi: Allocating from narrow strings", "[winapi][string allocation SECTION( "SysAllocStringByteLen with half-length parameter" ) { // Note: flawfinder warns about potentially using non-null terminating strings, - // but, in our case, the test string is guaranteed to be null-terminated! + // but, in our case, the test string is guaranteed to be null-terminated. expectedString = std::string{ testStr, std::strlen( testStr ) / 2 }; // flawfinder: ignore resultString = SysAllocStringByteLen( testStr, expectedString.size() ); } diff --git a/tests/src/utils/filesystem.hpp b/tests/src/utils/filesystem.hpp index 628af3fe..48ca71af 100644 --- a/tests/src/utils/filesystem.hpp +++ b/tests/src/utils/filesystem.hpp @@ -28,11 +28,6 @@ #include #include -#include - -#if defined(__MINGW32__) && defined(_WIO_DEFINED) -#include "internal/fsutil.hpp" -#endif namespace bit7z { // NOLINT(modernize-concat-nested-namespaces) namespace test { diff --git a/tests/src/utils/shared_lib.hpp b/tests/src/utils/shared_lib.hpp index 560c1adb..41435a9b 100644 --- a/tests/src/utils/shared_lib.hpp +++ b/tests/src/utils/shared_lib.hpp @@ -21,8 +21,10 @@ namespace test { inline auto sevenzip_lib_path() -> tstring { #ifdef BIT7Z_TESTS_USE_SYSTEM_7ZIP -#ifdef _WIN32 +#ifdef _WIN64 static const tstring lib_path = BIT7Z_STRING( "C:\\Program Files\\7-Zip\\7z.dll" ); +#elif defined( _WIN32 ) + static const tstring lib_path = BIT7Z_STRING( "C:\\Program Files (x86)\\7-Zip\\7z.dll" ); #elif defined( __linux__ ) static const tstring lib_path = "/usr/lib/p7zip/7z.so"; //default installation path of p7zip's shared library #else