From f76a9d1a586886be1bb42c48265d18abf0f144c2 Mon Sep 17 00:00:00 2001 From: Simon Gene Gottlieb Date: Thu, 6 Jun 2024 10:41:32 +0200 Subject: [PATCH 01/18] fix: add missing header (gcc14) gcc14 is complaining about unknown "std::find_if" function. (gcc13 works fine) --- src/bitinputarchive.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/bitinputarchive.cpp b/src/bitinputarchive.cpp index 294ce375..a73f1217 100644 --- a/src/bitinputarchive.cpp +++ b/src/bitinputarchive.cpp @@ -33,6 +33,8 @@ #include "internal/formatdetect.hpp" #endif +#include + using namespace NWindows; using namespace NArchive; @@ -420,4 +422,4 @@ auto BitInputArchive::ConstIterator::operator->() const noexcept -> BitInputArch BitInputArchive::ConstIterator::ConstIterator( uint32_t itemIndex, const BitInputArchive& itemArchive ) noexcept : mItemOffset( itemIndex, itemArchive ) {} -} // namespace bit7z \ No newline at end of file +} // namespace bit7z From f04d819e8eb82474532e3da4925e5dd247ea35bb Mon Sep 17 00:00:00 2001 From: Oz Date: Mon, 24 Jun 2024 19:42:48 +0200 Subject: [PATCH 02/18] Fix build failing when cross-compiling using MinGW on Linux Close issue #222 --- include/bit7z/bitwindows.hpp | 2 +- src/internal/stringutil.cpp | 2 +- tests/src/utils/filesystem.hpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bit7z/bitwindows.hpp b/include/bit7z/bitwindows.hpp index a86edca6..5849b956 100644 --- a/include/bit7z/bitwindows.hpp +++ b/include/bit7z/bitwindows.hpp @@ -18,7 +18,7 @@ #ifndef NOMINMAX #define NOMINMAX #endif -#include +#include #include #else diff --git a/src/internal/stringutil.cpp b/src/internal/stringutil.cpp index 9a0a3f44..2a6dfc3b 100644 --- a/src/internal/stringutil.cpp +++ b/src/internal/stringutil.cpp @@ -15,7 +15,7 @@ #include "internal/stringutil.hpp" #ifdef _WIN32 -#include +#include #ifdef BIT7Z_USE_SYSTEM_CODEPAGE #define CODEPAGE CP_ACP #define CODEPAGE_WC_FLAGS WC_NO_BEST_FIT_CHARS diff --git a/tests/src/utils/filesystem.hpp b/tests/src/utils/filesystem.hpp index 48ca71af..4b1e21aa 100644 --- a/tests/src/utils/filesystem.hpp +++ b/tests/src/utils/filesystem.hpp @@ -19,7 +19,7 @@ #ifdef _WIN32 #include -#include +#include #elif defined( __APPLE__ ) #include #include // for proc_pidpath and PROC_PIDPATHINFO_MAXSIZE From f35e1bdd85b0f84ab620568cb9748c795a3b9549 Mon Sep 17 00:00:00 2001 From: Oz Date: Wed, 10 Jul 2024 19:36:08 +0200 Subject: [PATCH 03/18] Fix conversion from FILETIME to file_time_type when using libstdc++ --- src/internal/dateutil.cpp | 25 +++++++++++++++++-------- src/internal/dateutil.hpp | 2 +- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/src/internal/dateutil.cpp b/src/internal/dateutil.cpp index fecbb0bc..11c24b7e 100644 --- a/src/internal/dateutil.cpp +++ b/src/internal/dateutil.cpp @@ -20,23 +20,32 @@ using FileTimeDuration = std::chrono::duration< int64_t, FileTimeTickRate >; // Seconds between 01/01/1601 (NT epoch) and 01/01/1970 (Unix epoch): constexpr std::chrono::seconds nt_to_unix_epoch{ -11644473600 }; -#ifndef _WIN32 +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM ) && defined( __GLIBCXX__ ) +// Seconds between 01/01/1970 (Unix epoch) and 01/01/2174 (libstdc++'s file_clock epoch). +constexpr std::chrono::seconds libstdcpp_file_clock_epoch{ -6437664000 }; +#endif +#ifndef _WIN32 auto FILETIME_to_file_time_type( FILETIME fileTime ) -> fs::file_time_type { const FileTimeDuration fileTimeDuration{ - ( static_cast< int64_t >( fileTime.dwHighDateTime ) << 32 ) + fileTime.dwLowDateTime + ( static_cast< std::uint64_t >( fileTime.dwHighDateTime ) << 32ull ) + fileTime.dwLowDateTime }; - const auto unixEpoch = fileTimeDuration + nt_to_unix_epoch; - return fs::file_time_type{ std::chrono::duration_cast< std::chrono::system_clock::duration >( unixEpoch ) }; + const auto unixFileTime = fileTimeDuration + nt_to_unix_epoch; + const auto systemFileTime = std::chrono::duration_cast< fs::file_time_type::clock::duration >( unixFileTime ); +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM ) && defined( __GLIBCXX__ ) + return fs::file_time_type{ systemFileTime + libstdcpp_file_clock_epoch }; +#else + return fs::file_time_type{ systemFileTime }; +#endif } -auto time_to_FILETIME( std::time_t timeValue ) -> FILETIME { +auto time_to_FILETIME( std::time_t value ) -> FILETIME { // NOLINTNEXTLINE(*-magic-numbers) - const uint64_t timeInSeconds = ( static_cast< uint64_t >( timeValue ) * 10000000ull ) + 116444736000000000; + const std::uint64_t timeInSeconds = ( static_cast< std::uint64_t >( value ) * 10000000ull ) + 116444736000000000ull; FILETIME fileTime{}; fileTime.dwLowDateTime = static_cast< DWORD >( timeInSeconds ); - fileTime.dwHighDateTime = static_cast< DWORD >( timeInSeconds >> 32 ); + fileTime.dwHighDateTime = static_cast< DWORD >( timeInSeconds >> 32ull ); return fileTime; } @@ -44,7 +53,7 @@ auto time_to_FILETIME( std::time_t timeValue ) -> FILETIME { auto FILETIME_to_time_type( FILETIME fileTime ) -> time_type { const FileTimeDuration fileTimeDuration{ - ( static_cast< int64_t >( fileTime.dwHighDateTime ) << 32 ) + fileTime.dwLowDateTime + ( static_cast< std::uint64_t >( fileTime.dwHighDateTime ) << 32ull ) + fileTime.dwLowDateTime }; const auto unixEpoch = fileTimeDuration + nt_to_unix_epoch; diff --git a/src/internal/dateutil.hpp b/src/internal/dateutil.hpp index defc2f19..0551402f 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( std::time_t timeValue ) -> FILETIME; +auto time_to_FILETIME( std::time_t value ) -> FILETIME; #endif From 6f1248cdec39f2316393c7b00b2fbd72f018c8e5 Mon Sep 17 00:00:00 2001 From: Oz Date: Wed, 10 Jul 2024 19:37:37 +0200 Subject: [PATCH 04/18] Rewrite directory indexing algorithm to not use recursion Fixes stack overflow issues when indexing deeply nested directory structures (close issue #223) --- src/internal/fsindexer.cpp | 48 +++++----- src/internal/fsindexer.hpp | 4 +- tests/src/test_bititemsvector.cpp | 154 ++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+), 27 deletions(-) diff --git a/src/internal/fsindexer.cpp b/src/internal/fsindexer.cpp index ec19c8ac..833f83fa 100644 --- a/src/internal/fsindexer.cpp +++ b/src/internal/fsindexer.cpp @@ -13,6 +13,8 @@ #include "bitexception.hpp" #include "internal/fsindexer.hpp" #include "internal/fsutil.hpp" +#include "internal/genericinputitem.hpp" +#include "internal/stringutil.hpp" namespace bit7z { // NOLINT(modernize-concat-nested-namespaces) namespace filesystem { @@ -33,44 +35,42 @@ FilesystemIndexer::FilesystemIndexer( FilesystemItem directory, } // NOTE: It indexes all the items whose metadata are needed in the archive to be created! -// NOLINTNEXTLINE(misc-no-recursion) -void FilesystemIndexer::listDirectoryItems( vector< unique_ptr< GenericInputItem > >& result, - bool recursive, - const fs::path& prefix ) { - fs::path path = mDirItem.filesystemPath(); - if ( !prefix.empty() ) { - path = path / prefix; - } +void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< GenericInputItem > >& result, + bool recursive ) { const bool includeRootPath = mFilter.empty() || !mDirItem.filesystemPath().has_parent_path() || mDirItem.inArchivePath().filename() != mDirItem.filesystemName(); const bool shouldIncludeMatchedItems = mPolicy == FilterPolicy::Include; + + fs::path basePath = mDirItem.filesystemPath(); std::error_code error; - for ( const auto& currentEntry : fs::directory_iterator( path, error ) ) { - auto searchPath = includeRootPath ? mDirItem.inArchivePath() : fs::path{}; - if ( !prefix.empty() ) { - searchPath = searchPath.empty() ? prefix : searchPath / prefix; - } + for ( auto iterator = fs::recursive_directory_iterator{ basePath, error }; + iterator != fs::recursive_directory_iterator{}; + ++iterator ) { + const auto& currentEntry = *iterator; + const auto& itemPath = currentEntry.path(); + + const auto prefix = fs::relative( itemPath, basePath, error ).remove_filename(); + const auto searchPath = includeRootPath ? mDirItem.inArchivePath() / prefix : prefix; + + const auto itemIsDir = currentEntry.is_directory( error ); + const auto itemName = path_to_tstring( itemPath.filename() ); - const FilesystemItem currentItem{ currentEntry, searchPath, mSymlinkPolicy }; /* An item matches if: * - Its name matches the wildcard pattern, and * - Either is a file, or we are interested also to include folders in the index. * * Note: The boolean expression uses short-circuiting to optimize the evaluation. */ - const bool itemMatches = ( !mOnlyFiles || !currentItem.isDir() ) && - fsutil::wildcard_match( mFilter, currentItem.name() ); + const bool itemMatches = ( !mOnlyFiles || !itemIsDir ) && fsutil::wildcard_match( mFilter, itemName ); if ( itemMatches == shouldIncludeMatchedItems ) { - result.emplace_back( std::make_unique< FilesystemItem >( currentItem ) ); + result.emplace_back( std::make_unique< FilesystemItem >( currentEntry, searchPath, mSymlinkPolicy ) ); } - if ( currentItem.isDir() && ( recursive || ( itemMatches == shouldIncludeMatchedItems ) ) ) { - //currentItem is a directory, and we must list it only if: - // > indexing is done recursively - // > indexing is not recursive, but the directory name matched the filter. - const fs::path nextDir = prefix.empty() ? - currentItem.filesystemName() : prefix / currentItem.filesystemName(); - listDirectoryItems( result, true, nextDir ); + /* We don't need to recurse inside the current item if: + * - it is not a directory; or + * - we are not indexing recursively, and the directory's name doesn't match the wildcard filter. */ + if ( !itemIsDir || ( !recursive && ( itemMatches != shouldIncludeMatchedItems ) ) ) { + iterator.disable_recursion_pending(); } } } diff --git a/src/internal/fsindexer.hpp b/src/internal/fsindexer.hpp index edcfd4ec..1aeebbef 100644 --- a/src/internal/fsindexer.hpp +++ b/src/internal/fsindexer.hpp @@ -31,9 +31,7 @@ class FilesystemIndexer final { SymlinkPolicy symlinkPolicy = SymlinkPolicy::Follow, bool onlyFiles = false ); - void listDirectoryItems( vector< unique_ptr< GenericInputItem > >& result, - bool recursive, - const fs::path& prefix = fs::path{} ); + void listDirectoryItems( std::vector< std::unique_ptr< GenericInputItem > >& result, bool recursive ); private: FilesystemItem mDirItem; diff --git a/tests/src/test_bititemsvector.cpp b/tests/src/test_bititemsvector.cpp index bf5e0a67..50a7b4fd 100644 --- a/tests/src/test_bititemsvector.cpp +++ b/tests/src/test_bititemsvector.cpp @@ -224,6 +224,10 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (only files)", "[bititems TEST_CASE( "BitItemsVector: Indexing a valid directory (retaining folder structure)", "[bititemsvector]" ) { static const TestDirectory testDir{ test_filesystem_dir }; + /* NOTE: BitItemsVector uses the retainFolderStructure option only to decide if it must include + * the root folder in the in-archive paths; the actual folder structure is still kept here, + * and will be discarded by the extract callback (e.g., see FileExtractCallback). */ + // TODO: Rationalize the handling of retainFolderStructure. IndexingOptions options{}; options.retainFolderStructure = true; @@ -1289,6 +1293,32 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (relative path)", "[bitit "test_filesystem/folder/subfolder2/frequency.xlsx" } }, + TestInputPath{ + "folder/..", + { + "italy.svg", + "Lorem Ipsum.pdf", + "noext", + BIT7Z_NATIVE_STRING( "σαράντα δύο.txt" ), + "dot.folder", + "dot.folder/hello.json", + "empty", + "folder", + "folder/clouds.jpg", + "folder/subfolder", + "folder/subfolder2", + "folder/subfolder2/homework.doc", + "folder/subfolder2/The quick brown fox.pdf", + "folder/subfolder2/frequency.xlsx" + } + }, + TestInputPath{ + "folder/../dot.folder", + { + "dot.folder", + "dot.folder/hello.json" + } + }, TestInputPath{ "../test_filesystem/empty", { "empty" } }, TestInputPath{ "../test_filesystem/folder", @@ -1302,6 +1332,18 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (relative path)", "[bitit "folder/subfolder2/frequency.xlsx" } }, + TestInputPath{ + "folder/subfolder2/../", + { + "folder", + "folder/clouds.jpg", + "folder/subfolder", + "folder/subfolder2", + "folder/subfolder2/homework.doc", + "folder/subfolder2/The quick brown fox.pdf", + "folder/subfolder2/frequency.xlsx" + } + }, TestInputPath{ "../test_filesystem/folder/subfolder2", { @@ -1350,6 +1392,32 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (relative path, non-recur "test_filesystem/folder/subfolder2/frequency.xlsx" } }, + TestInputPath{ + "folder/..", + { + "italy.svg", + "Lorem Ipsum.pdf", + "noext", + BIT7Z_NATIVE_STRING( "σαράντα δύο.txt" ), + "dot.folder", + "dot.folder/hello.json", + "empty", + "folder", + "folder/clouds.jpg", + "folder/subfolder", + "folder/subfolder2", + "folder/subfolder2/homework.doc", + "folder/subfolder2/The quick brown fox.pdf", + "folder/subfolder2/frequency.xlsx" + } + }, + TestInputPath{ + "folder/../dot.folder", + { + "dot.folder", + "dot.folder/hello.json" + } + }, TestInputPath{ "../test_filesystem/empty", { "empty" } }, TestInputPath{ "../test_filesystem/folder", @@ -1363,6 +1431,18 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (relative path, non-recur "folder/subfolder2/frequency.xlsx" } }, + TestInputPath{ + "folder/subfolder2/../", + { + "folder", + "folder/clouds.jpg", + "folder/subfolder", + "folder/subfolder2", + "folder/subfolder2/homework.doc", + "folder/subfolder2/The quick brown fox.pdf", + "folder/subfolder2/frequency.xlsx" + } + }, TestInputPath{ "../test_filesystem/folder/subfolder2", { @@ -1409,6 +1489,46 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (custom path mapping)", " "custom_folder/folder/subfolder2/frequency.xlsx" } }, + TestInputPath{ + "folder/..", + { + "custom_folder", + "custom_folder/dot.folder", + "custom_folder/dot.folder/hello.json", + "custom_folder/italy.svg", + "custom_folder/Lorem Ipsum.pdf", + "custom_folder/noext", + BIT7Z_NATIVE_STRING( "custom_folder/σαράντα δύο.txt" ), + "custom_folder/empty", + "custom_folder/folder", + "custom_folder/folder/clouds.jpg", + "custom_folder/folder/subfolder", + "custom_folder/folder/subfolder2", + "custom_folder/folder/subfolder2/homework.doc", + "custom_folder/folder/subfolder2/The quick brown fox.pdf", + "custom_folder/folder/subfolder2/frequency.xlsx" + } + }, + TestInputPath{ + "folder/../", + { + "custom_folder", + "custom_folder/dot.folder", + "custom_folder/dot.folder/hello.json", + "custom_folder/italy.svg", + "custom_folder/Lorem Ipsum.pdf", + "custom_folder/noext", + BIT7Z_NATIVE_STRING( "custom_folder/σαράντα δύο.txt" ), + "custom_folder/empty", + "custom_folder/folder", + "custom_folder/folder/clouds.jpg", + "custom_folder/folder/subfolder", + "custom_folder/folder/subfolder2", + "custom_folder/folder/subfolder2/homework.doc", + "custom_folder/folder/subfolder2/The quick brown fox.pdf", + "custom_folder/folder/subfolder2/frequency.xlsx" + } + }, TestInputPath{ "empty", { "custom_folder" } }, TestInputPath{ "./empty", { "custom_folder" } }, TestInputPath{ @@ -1435,6 +1555,30 @@ TEST_CASE( "BitItemsVector: Indexing a valid directory (custom path mapping)", " "custom_folder/subfolder2/frequency.xlsx" } }, + TestInputPath{ + "./folder/subfolder2/..", + { + "custom_folder", + "custom_folder/clouds.jpg", + "custom_folder/subfolder", + "custom_folder/subfolder2", + "custom_folder/subfolder2/homework.doc", + "custom_folder/subfolder2/The quick brown fox.pdf", + "custom_folder/subfolder2/frequency.xlsx" + } + }, + TestInputPath{ + "./folder/subfolder2/../", + { + "custom_folder", + "custom_folder/clouds.jpg", + "custom_folder/subfolder", + "custom_folder/subfolder2", + "custom_folder/subfolder2/homework.doc", + "custom_folder/subfolder2/The quick brown fox.pdf", + "custom_folder/subfolder2/frequency.xlsx" + } + }, TestInputPath{ "folder/subfolder2", { @@ -1823,9 +1967,11 @@ TEST_CASE( "BitItemsVector: Indexing a directory as a file should fail", "[bitit BitItemsVector itemsVector; REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "." ) ) ); REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "dot.folder" ) ) ); + REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "dot.folder/../" ) ) ); REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "empty" ) ) ); REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "folder" ) ) ); REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "folder/subfolder" ) ) ); + REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "folder/subfolder/../.." ) ) ); REQUIRE_THROWS( itemsVector.indexFile( BIT7Z_STRING( "folder/subfolder2" ) ) ); } @@ -1849,10 +1995,14 @@ TEST_CASE( "BitItemsVector: Indexing a single file", "[bititemsvector]" ) { const auto testInput = GENERATE( TestFile{ "Lorem Ipsum.pdf", "Lorem Ipsum.pdf" }, + TestFile{ "empty/../Lorem Ipsum.pdf", "Lorem Ipsum.pdf" }, + TestFile{ "folder/subfolder2/../../Lorem Ipsum.pdf", "Lorem Ipsum.pdf" }, TestFile{ "italy.svg", "italy.svg" }, TestFile{ "noext", "noext" }, TestFile{ BIT7Z_NATIVE_STRING( "σαράντα δύο.txt" ), BIT7Z_NATIVE_STRING( "σαράντα δύο.txt" ) }, TestFile{ "folder/clouds.jpg", "folder/clouds.jpg" }, + TestFile{ "dot.folder/../folder/clouds.jpg", "clouds.jpg" }, + TestFile{ "folder/subfolder2/../clouds.jpg", "clouds.jpg" }, TestFile{ "folder/subfolder2/homework.doc", "folder/subfolder2/homework.doc" }, TestFile{ "folder/subfolder2/The quick brown fox.pdf", "folder/subfolder2/The quick brown fox.pdf" }, TestFile{ "folder/subfolder2/frequency.xlsx", "folder/subfolder2/frequency.xlsx" }, @@ -1862,9 +2012,13 @@ TEST_CASE( "BitItemsVector: Indexing a single file", "[bititemsvector]" ) { const auto testInput = GENERATE( TestFile{ "Lorem Ipsum.pdf", "Lorem Ipsum.pdf" }, + TestFile{ "empty/../Lorem Ipsum.pdf", "Lorem Ipsum.pdf" }, + TestFile{ "folder/subfolder2/../../Lorem Ipsum.pdf", "Lorem Ipsum.pdf" }, TestFile{ "italy.svg", "italy.svg" }, TestFile{ "noext", "noext" }, TestFile{ "folder/clouds.jpg", "folder/clouds.jpg" }, + TestFile{ "dot.folder/../folder/clouds.jpg", "clouds.jpg" }, + TestFile{ "folder/subfolder2/../clouds.jpg", "clouds.jpg" }, TestFile{ "folder/subfolder2/homework.doc", "folder/subfolder2/homework.doc" }, TestFile{ "folder/subfolder2/The quick brown fox.pdf", "folder/subfolder2/The quick brown fox.pdf" }, TestFile{ "folder/subfolder2/frequency.xlsx", "folder/subfolder2/frequency.xlsx" }, From 2cdfac175ff782919d60285d1ab5e4863507f7a8 Mon Sep 17 00:00:00 2001 From: Oz Date: Wed, 10 Jul 2024 19:44:05 +0200 Subject: [PATCH 05/18] Update project version in CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a3cc671f..2eb86345 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,7 +5,7 @@ cmake_minimum_required( VERSION 3.14 ) project( bit7z - VERSION 4.0.7 + VERSION 4.0.8 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" ) From 6b352202a32c92a5a0f5905d0986fc3176534444 Mon Sep 17 00:00:00 2001 From: Oz Date: Thu, 11 Jul 2024 12:35:10 +0200 Subject: [PATCH 06/18] Skip inaccessible folders while indexing a directory --- src/internal/fsindexer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/internal/fsindexer.cpp b/src/internal/fsindexer.cpp index 833f83fa..13df9a79 100644 --- a/src/internal/fsindexer.cpp +++ b/src/internal/fsindexer.cpp @@ -44,7 +44,7 @@ void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< Generi fs::path basePath = mDirItem.filesystemPath(); std::error_code error; - for ( auto iterator = fs::recursive_directory_iterator{ basePath, error }; + for ( auto iterator = fs::recursive_directory_iterator{ basePath, fs::directory_options::skip_permission_denied, error }; iterator != fs::recursive_directory_iterator{}; ++iterator ) { const auto& currentEntry = *iterator; From 1adc58bdc4c8744666f94024e27716e1f239c16f Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 14 Jul 2024 14:34:09 +0200 Subject: [PATCH 07/18] Optimize BitArchiveReader::items() --- src/bitarchivereader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitarchivereader.cpp b/src/bitarchivereader.cpp index 74e1334b..3c318712 100644 --- a/src/bitarchivereader.cpp +++ b/src/bitarchivereader.cpp @@ -64,7 +64,7 @@ auto BitArchiveReader::items() const -> std::vector< BitArchiveItemInfo > { item.setProperty( property, propertyValue ); } } - result.push_back( std::move( item ) ); + result.emplace_back( std::move( item ) ); } return result; } From e1e5ee3f4b1b543648f18f020a70e98488a90476 Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 14 Jul 2024 14:34:45 +0200 Subject: [PATCH 08/18] Optimize indexing of directories --- src/internal/fsindexer.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/internal/fsindexer.cpp b/src/internal/fsindexer.cpp index 13df9a79..3eac9938 100644 --- a/src/internal/fsindexer.cpp +++ b/src/internal/fsindexer.cpp @@ -50,9 +50,6 @@ void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< Generi const auto& currentEntry = *iterator; const auto& itemPath = currentEntry.path(); - const auto prefix = fs::relative( itemPath, basePath, error ).remove_filename(); - const auto searchPath = includeRootPath ? mDirItem.inArchivePath() / prefix : prefix; - const auto itemIsDir = currentEntry.is_directory( error ); const auto itemName = path_to_tstring( itemPath.filename() ); @@ -63,6 +60,8 @@ void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< Generi * Note: The boolean expression uses short-circuiting to optimize the evaluation. */ const bool itemMatches = ( !mOnlyFiles || !itemIsDir ) && fsutil::wildcard_match( mFilter, itemName ); if ( itemMatches == shouldIncludeMatchedItems ) { + const auto prefix = fs::relative( itemPath, basePath, error ).remove_filename(); + const auto searchPath = includeRootPath ? mDirItem.inArchivePath() / prefix : prefix; result.emplace_back( std::make_unique< FilesystemItem >( currentEntry, searchPath, mSymlinkPolicy ) ); } From f48875f63c69c9f38b37ac03bce282027c6b0c77 Mon Sep 17 00:00:00 2001 From: Oz Date: Fri, 26 Jul 2024 16:02:05 +0200 Subject: [PATCH 09/18] [Test] Disable buffering of file streams to improve performance --- src/internal/cfileinstream.cpp | 13 ++++++------- src/internal/cfileinstream.hpp | 2 -- src/internal/cfileoutstream.cpp | 10 +++++++--- src/internal/cfileoutstream.hpp | 3 --- 4 files changed, 13 insertions(+), 15 deletions(-) diff --git a/src/internal/cfileinstream.cpp b/src/internal/cfileinstream.cpp index 0c862656..94e47527 100644 --- a/src/internal/cfileinstream.cpp +++ b/src/internal/cfileinstream.cpp @@ -16,14 +16,13 @@ namespace bit7z { -CFileInStream::CFileInStream( const fs::path& filePath ) : CStdInStream( mFileStream ), mBuffer{} { +CFileInStream::CFileInStream( const fs::path& filePath ) : CStdInStream( mFileStream ) { + /* Disabling std::ifstream's buffering, as unbuffered IO gives better performance + * with the block sizes read/written by 7-Zip. + * Note: we need to do this before and after opening the file (https://stackoverflow.com/a/59161297/3497024). */ + mFileStream.rdbuf()->pubsetbuf( nullptr, 0 ); openFile( filePath ); - - /* By default, file stream performance is relatively poor due to the default buffer size used - * (e.g., GCC uses a small 1024-bytes buffer). - * This is a known problem (see https://stackoverflow.com/questions/26095160/why-are-stdfstreams-so-slow). - * We make the underlying file stream use a bigger buffer (1 MiB) for optimizing the reading of big files. */ - mFileStream.rdbuf()->pubsetbuf( mBuffer.data(), kBufferSize ); + mFileStream.rdbuf()->pubsetbuf( nullptr, 0 ); } void CFileInStream::openFile( const fs::path& filePath ) { diff --git a/src/internal/cfileinstream.hpp b/src/internal/cfileinstream.hpp index 39ea16fb..a309a000 100644 --- a/src/internal/cfileinstream.hpp +++ b/src/internal/cfileinstream.hpp @@ -26,8 +26,6 @@ class CFileInStream : public CStdInStream { private: fs::ifstream mFileStream; - static constexpr auto kBufferSize = 1024 * 1024; // 1 MiB - std::array< char, kBufferSize > mBuffer; }; } // namespace bit7z diff --git a/src/internal/cfileoutstream.cpp b/src/internal/cfileoutstream.cpp index 176dc906..4ca21767 100644 --- a/src/internal/cfileoutstream.cpp +++ b/src/internal/cfileoutstream.cpp @@ -19,7 +19,7 @@ namespace bit7z { CFileOutStream::CFileOutStream( fs::path filePath, bool createAlways ) - : CStdOutStream( mFileStream ), mFilePath{ std::move( filePath ) }, mBuffer{} { + : CStdOutStream( mFileStream ), mFilePath{ std::move( filePath ) } { std::error_code error; if ( !createAlways && fs::exists( mFilePath, error ) ) { if ( !error ) { @@ -28,6 +28,11 @@ CFileOutStream::CFileOutStream( fs::path filePath, bool createAlways ) } throw BitException( "Failed to create the output file", error, path_to_tstring( mFilePath ) ); } + + /* Disabling std::ofstream's buffering, as unbuffered IO gives better performance + * with the block sizes read/written by 7-Zip. + * Note: we need to do this before and after opening the file (https://stackoverflow.com/a/59161297/3497024). */ + mFileStream.rdbuf()->pubsetbuf( nullptr, 0 ); mFileStream.open( mFilePath, std::ios::binary | std::ios::trunc ); // flawfinder: ignore if ( mFileStream.fail() ) { #if defined( __MINGW32__ ) || defined( __MINGW64__ ) @@ -37,8 +42,7 @@ CFileOutStream::CFileOutStream( fs::path filePath, bool createAlways ) throw BitException( "Failed to open the output file", last_error_code(), path_to_tstring( mFilePath ) ); #endif } - - mFileStream.rdbuf()->pubsetbuf( mBuffer.data(), kBufferSize ); + mFileStream.rdbuf()->pubsetbuf( nullptr, 0 ); } auto CFileOutStream::fail() const -> bool { diff --git a/src/internal/cfileoutstream.hpp b/src/internal/cfileoutstream.hpp index 33815bf7..5322ea7b 100644 --- a/src/internal/cfileoutstream.hpp +++ b/src/internal/cfileoutstream.hpp @@ -31,9 +31,6 @@ class CFileOutStream : public CStdOutStream { private: fs::path mFilePath; fs::ofstream mFileStream; - - static constexpr auto kBufferSize = 1024 * 1024; // 1 MiB - std::array< char, kBufferSize > mBuffer; }; } // namespace bit7z From f06222b65c15e7408ca0f7a6a2ed41965d2f6543 Mon Sep 17 00:00:00 2001 From: Oz Date: Fri, 26 Jul 2024 21:16:29 +0200 Subject: [PATCH 10/18] [Test] Fix dateutil tests --- tests/src/test_dateutil.cpp | 78 ++++++++++++++++++++++++++++++++---- tests/src/utils/datetime.hpp | 57 ++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 tests/src/utils/datetime.hpp diff --git a/tests/src/test_dateutil.cpp b/tests/src/test_dateutil.cpp index 86e25753..3e4dc660 100644 --- a/tests/src/test_dateutil.cpp +++ b/tests/src/test_dateutil.cpp @@ -10,13 +10,30 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ -#if !defined(__GNUC__) || __GNUC__ >= 5 || defined(__clang__) #include +#ifndef _WIN32 +#include "utils/datetime.hpp" +#include "utils/shared_lib.hpp" + +#include +#include +#endif + #include +#ifndef _WIN32 +#include +#endif +#include // For std::time_t (on MSVC 2015). + using namespace bit7z; +#ifndef _WIN32 +using namespace bit7z::test; +using namespace bit7z::test::filesystem; +#endif + /* Note: std::time_t is usually a UNIX timestamp, so we are using only dates after the UNIX epoch datetime. * We do not expect the conversion function to check whether the input is correct, * so we don't perform tests with wrong inputs. */ @@ -32,23 +49,23 @@ TEST_CASE( "fsutil: Date conversions", "[fsutil][date functions]" ) { using std::chrono::seconds; auto testDate = GENERATE( as< DateConversionTest >(), - DateConversionTest{ "21 December 2012, 12:00", 1356091200, { 3017121792, 30269298 } }, - DateConversionTest{ "1 January 1970, 00:00", 0, { 3577643008, 27111902 } } ); + DateConversionTest{ "13 February 2023, 20:54:25", 1676321665, { 1526060672, 31014893 } }, + DateConversionTest{ "21 December 2012, 12:00:00", 1356091200, { 3017121792, 30269298 } }, + DateConversionTest{ "1 January 1970, 00:00:00", 0, { 3577643008, 27111902 } } ); DYNAMIC_SECTION( "Date: " << testDate.name ) { #ifndef _WIN32 SECTION( "From std::time_t to FILETIME" ) { - auto output = time_to_FILETIME( testDate.dateTime ); - REQUIRE( output.dwHighDateTime == testDate.fileTime.dwHighDateTime ); - REQUIRE( output.dwLowDateTime == testDate.fileTime.dwLowDateTime ); + auto result = time_to_FILETIME( testDate.dateTime ); + REQUIRE( result.dwHighDateTime == testDate.fileTime.dwHighDateTime ); + REQUIRE( result.dwLowDateTime == testDate.fileTime.dwLowDateTime ); } #endif #ifndef _WIN32 SECTION( "From FILETIME to std::filesystem::file_time_type" ) { auto result = FILETIME_to_file_time_type( testDate.fileTime ); - auto output = duration_cast< seconds >( result.time_since_epoch() ).count(); - REQUIRE( output == testDate.dateTime ); + REQUIRE( as_unix_timestamp( result ) == testDate.dateTime ); } #endif @@ -59,4 +76,49 @@ TEST_CASE( "fsutil: Date conversions", "[fsutil][date functions]" ) { } } } + +#ifndef _WIN32 +TEST_CASE( "fsutil: Date conversion of current time should preserve information up to seconds", + "[fsutil][date functions]" ) { + const auto currentTime = std::chrono::system_clock::now(); + const auto unixTimestamp = as_unix_timestamp( currentTime ); + INFO( "Current time: " << unixTimestamp ) + + // Converting the current time to FILETIME + const auto asTimeT = std::chrono::system_clock::to_time_t( currentTime ); + const auto asFileTime = time_to_FILETIME( asTimeT ); + + SECTION( "Converting current FILETIME to a system_clock's time_point" ) { + const auto asSystemTimePoint = FILETIME_to_time_type( asFileTime ); + REQUIRE( unixTimestamp == as_unix_timestamp( asSystemTimePoint ) ); + } + + SECTION( "Converting current FILETIME to a file_clock's time_point" ) { + const auto asFileTimePoint = FILETIME_to_file_time_type( asFileTime ); + REQUIRE( unixTimestamp == as_unix_timestamp( asFileTimePoint ) ); + } +} + +TEST_CASE( "fsutil: Date conversion of last write time", "[fsutil][date functions]" ) { + const TestDirectory testDir{ fs::path{ test_archives_dir } / "extraction" / "single_file" }; + + const auto arcFileName = fs::path{ clouds.name }.concat( ".7z" ); + + const Bit7zLibrary lib{ test::sevenzip_lib_path() }; + BitArchiveReader info( lib, path_to_tstring( arcFileName ), BitFormat::SevenZip ); + + const auto item = info.itemAt( 0 ); + + const auto lastWriteTime = item.itemProperty( BitProperty::MTime ).getFileTime(); + INFO( "Last write time FILETIME: {" << lastWriteTime.dwHighDateTime << ", " << lastWriteTime.dwLowDateTime << "}") + const auto result = FILETIME_to_file_time_type( lastWriteTime ); + + const auto result_as_timestamp = as_unix_timestamp( result ); + INFO( "Last write file time_point: " << result_as_timestamp ) + + const auto result2 = item.lastWriteTime(); + const auto result2_as_timestamp = as_unix_timestamp( result2 ); + INFO( "Last write system time_point: " << result2_as_timestamp ) + REQUIRE( result_as_timestamp == result2_as_timestamp ); +} #endif \ No newline at end of file diff --git a/tests/src/utils/datetime.hpp b/tests/src/utils/datetime.hpp new file mode 100644 index 00000000..1a8c0f7a --- /dev/null +++ b/tests/src/utils/datetime.hpp @@ -0,0 +1,57 @@ +/* + * bit7z - A C++ static library to interface with the 7-zip shared libraries. + * Copyright (c) 2014-2024 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 DATETIME_HPP +#define DATETIME_HPP + +#ifndef _WIN32 + +#include + +#include +#include + +namespace bit7z { +namespace test { + +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM ) && defined( __GLIBCXX__ ) +constexpr std::chrono::seconds libstdcpp_file_clock_epoch{ 6437664000 }; +#endif + +template< + class Clock, + class Duration = typename Clock::duration +> +auto as_unix_timestamp( const std::chrono::time_point< Clock, Duration > timePoint ) -> std::uint64_t { + const auto asSeconds = std::chrono::duration_cast< std::chrono::seconds >( timePoint.time_since_epoch() ); + return static_cast< std::uint64_t >( asSeconds.count() ); +} + +#if defined( BIT7Z_USE_STANDARD_FILESYSTEM ) && defined( __GLIBCXX__ ) +template<> +inline auto as_unix_timestamp( const fs::file_time_type timePoint ) -> std::uint64_t { + const auto sinceEpoch = timePoint.time_since_epoch(); + const auto asSeconds = std::chrono::duration_cast< std::chrono::seconds >( sinceEpoch ); + const auto sinceUnixEpoch = libstdcpp_file_clock_epoch + asSeconds; + + auto nano = std::chrono::duration_cast< std::chrono::nanoseconds >( sinceEpoch - asSeconds ); + if ( nano < fs::file_time_type::duration::zero() ) { + return static_cast< std::uint64_t >( ( sinceUnixEpoch - std::chrono::seconds{ 1 } ).count() ); + } + + return static_cast< std::uint64_t >( sinceUnixEpoch.count() ); +} +#endif + +} // namespace test +} // namespace bit7z + +#endif + +#endif //DATETIME_HPP From eeffaae13544120bdb064a71cca4fe58816438d2 Mon Sep 17 00:00:00 2001 From: Oz Date: Wed, 31 Jul 2024 18:29:13 +0200 Subject: [PATCH 11/18] Add BitOutputArchive::addDirectoryContents method Close issue #226 --- include/bit7z/bitoutputarchive.hpp | 23 +++++++++++++++++++++++ src/bitoutputarchive.cpp | 17 +++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/include/bit7z/bitoutputarchive.hpp b/include/bit7z/bitoutputarchive.hpp index 5c53cfbe..7149c234 100644 --- a/include/bit7z/bitoutputarchive.hpp +++ b/include/bit7z/bitoutputarchive.hpp @@ -192,6 +192,29 @@ class BitOutputArchive { */ void addDirectory( const tstring& inDir ); + /** + * @brief Adds the contents of the given directory path that match the given wildcard filter. + * + * @param inDir the directory where to search for files to be added to the output archive. + * @param filter the wildcard filter to be used for searching the files. + * @param recursive recursively search the files in the given directory and all of its subdirectories. + */ + void addDirectoryContents( const tstring& inDir, const tstring& filter, bool recursive ); + + /** + * @brief Adds the contents of the given directory path that match the given wildcard filter. + * + * @param inDir the directory where to search for files to be added to the output archive. + * @param filter (optional) the wildcard filter to be used for searching the files. + * @param recursive (optional) recursively search the files in the given directory + * and all of its subdirectories. + * @param policy (optional) the filtering policy to be applied to the matched items. + */ + void addDirectoryContents( const tstring& inDir, + const tstring& filter = BIT7Z_STRING( "*" ), + FilterPolicy policy = FilterPolicy::Include, + bool recursive = true ); + /** * @brief Compresses all the items added to this object to the specified archive file path. * diff --git a/src/bitoutputarchive.cpp b/src/bitoutputarchive.cpp index 48edd2dd..0872ecda 100644 --- a/src/bitoutputarchive.cpp +++ b/src/bitoutputarchive.cpp @@ -132,6 +132,23 @@ void BitOutputArchive::addDirectory( const tstring& inDir ) { mNewItemsVector.indexDirectory( tstring_to_path( inDir ), BIT7Z_STRING( "" ), FilterPolicy::Include, options ); } +void BitOutputArchive::addDirectoryContents( const tstring& inDir, const tstring& filter, bool recursive ) { + addDirectoryContents( inDir, filter, FilterPolicy::Include, recursive ); +} + +void BitOutputArchive::addDirectoryContents( const tstring& inDir, + const tstring& filter, + FilterPolicy policy, + bool recursive ) { + IndexingOptions options{}; + options.recursive = recursive; + options.onlyFiles = !recursive; + options.retainFolderStructure = mArchiveCreator.retainDirectories(); + options.followSymlinks = !mArchiveCreator.storeSymbolicLinks(); + std::error_code error; + mNewItemsVector.indexDirectory( fs::absolute( tstring_to_path( inDir ), error ), filter, policy, options ); +} + auto BitOutputArchive::initOutArchive() const -> CMyComPtr< IOutArchive > { CMyComPtr< IOutArchive > newArc; if ( mInputArchive == nullptr ) { From b3d00179a61ccd0657de0f2c52b75355a639ec81 Mon Sep 17 00:00:00 2001 From: Oz Date: Wed, 31 Jul 2024 21:46:19 +0200 Subject: [PATCH 12/18] Improve BitOutputDirectory::addDirectory description comment --- include/bit7z/bitoutputarchive.hpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bit7z/bitoutputarchive.hpp b/include/bit7z/bitoutputarchive.hpp index 7149c234..db14eaba 100644 --- a/include/bit7z/bitoutputarchive.hpp +++ b/include/bit7z/bitoutputarchive.hpp @@ -186,9 +186,9 @@ class BitOutputArchive { bool recursive = true ); /** - * @brief Adds all the items inside the given directory path. + * @brief Adds the given directory path and all its content. * - * @param inDir the directory where to search for items to be added to the output archive. + * @param inDir the path of the directory to be added to the archive. */ void addDirectory( const tstring& inDir ); From 084b5ddb90b1eb69a5337cf9f9a70f97282c9340 Mon Sep 17 00:00:00 2001 From: Oz Date: Tue, 18 Jun 2024 14:53:09 +0200 Subject: [PATCH 13/18] Fix support for BSD operating systems Close issue #230 --- src/internal/formatdetect.cpp | 2 ++ src/internal/fsutil.cpp | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/internal/formatdetect.cpp b/src/internal/formatdetect.cpp index 35670cb3..66b75114 100644 --- a/src/internal/formatdetect.cpp +++ b/src/internal/formatdetect.cpp @@ -343,7 +343,9 @@ struct OffsetSignature { #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 the _has_builtin macro. +#ifndef bswap64 #define bswap64 __builtin_bswap64 +#endif #else static inline uint64_t bswap64( uint64_t x ) { return ((x << 56) & 0xff00000000000000ULL) | diff --git a/src/internal/fsutil.cpp b/src/internal/fsutil.cpp index 6b8c127d..ffbd9f3a 100644 --- a/src/internal/fsutil.cpp +++ b/src/internal/fsutil.cpp @@ -179,7 +179,8 @@ static const mode_t global_umask = []() noexcept -> mode_t { #endif #ifndef _WIN32 -#ifdef __APPLE__ +#if defined( __APPLE__ ) || defined( BSD ) || \ + defined( __FreeBSD__ ) || defined( __NetBSD__ ) || defined( __OpenBSD__ ) || defined( __DragonFly__ ) using stat_t = struct stat; const auto os_lstat = &lstat; const auto os_stat = &stat; From e858e81de7af5254f43f8793b67d18d9fc17279a Mon Sep 17 00:00:00 2001 From: Oz Date: Sun, 11 Aug 2024 15:00:47 +0200 Subject: [PATCH 14/18] Add method compressDirectoryContents to BitFileCompressor Issue #226 --- include/bit7z/bitfilecompressor.hpp | 15 +++++++++++++++ src/bitfilecompressor.cpp | 12 ++++++++++++ 2 files changed, 27 insertions(+) diff --git a/include/bit7z/bitfilecompressor.hpp b/include/bit7z/bitfilecompressor.hpp index 6a07e03b..dffcf9e0 100644 --- a/include/bit7z/bitfilecompressor.hpp +++ b/include/bit7z/bitfilecompressor.hpp @@ -104,6 +104,21 @@ class BitFileCompressor final : public BitCompressor< const tstring& > { */ void compressDirectory( const tstring& inDir, const tstring& outFile ) const; + /** + * @brief Compresses the contents of a directory. + * + * @note Unlike compressFiles, this method includes also the metadata of the sub-folders. + * + * @param inDir the path (relative or absolute) to the input directory. + * @param outFile the path (relative or absolute) to the output archive file. + * @param recursive (optional) if true, it searches the contents inside the sub-folders of inDir. + * @param filter (optional) the filter to use when searching the contents inside inDir. + */ + void compressDirectoryContents( const tstring& inDir, + const tstring& outFile, + bool recursive = true, + const tstring& filter = BIT7Z_STRING( "*" ) ) const; + /* Compression from the file system to standard streams. */ /** diff --git a/src/bitfilecompressor.cpp b/src/bitfilecompressor.cpp index 8b3d0db2..fe0359f6 100644 --- a/src/bitfilecompressor.cpp +++ b/src/bitfilecompressor.cpp @@ -70,6 +70,18 @@ void BitFileCompressor::compressDirectory( const tstring& inDir, const tstring& outputArchive.compressTo( outFile ); } +void BitFileCompressor::compressDirectoryContents( const tstring& inDir, + const tstring& outFile, + bool recursive, + const tstring& filter) const { + if ( !compressionFormat().hasFeature( FormatFeatures::MultipleFiles ) ) { + throw BitException( "Cannot compress multiple files", make_error_code( BitError::UnsupportedOperation ) ); + } + BitOutputArchive outputArchive{ *this, outFile }; + outputArchive.addDirectoryContents( inDir, filter, recursive ); + outputArchive.compressTo( outFile ); +} + /* from filesystem to stream */ void BitFileCompressor::compress( const std::vector< tstring >& inPaths, std::ostream& outStream ) const { From 18082764a70c6228c7e57856881247bdb86f2ebb Mon Sep 17 00:00:00 2001 From: Oz Date: Fri, 16 Aug 2024 13:48:46 +0200 Subject: [PATCH 15/18] [Test] Optimize directory indexing --- src/internal/fsindexer.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/internal/fsindexer.cpp b/src/internal/fsindexer.cpp index 3eac9938..c5eb8cc9 100644 --- a/src/internal/fsindexer.cpp +++ b/src/internal/fsindexer.cpp @@ -34,6 +34,12 @@ FilesystemIndexer::FilesystemIndexer( FilesystemItem directory, } } +inline auto countItemsInPath( const fs::path& path ) -> std::size_t { + std::error_code error; + auto begin = fs::recursive_directory_iterator{ path, fs::directory_options::skip_permission_denied, error }; + return error ? 0 : static_cast< std::size_t >( std::distance( begin, fs::recursive_directory_iterator{} ) ); +} + // NOTE: It indexes all the items whose metadata are needed in the archive to be created! void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< GenericInputItem > >& result, bool recursive ) { @@ -44,6 +50,7 @@ void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< Generi fs::path basePath = mDirItem.filesystemPath(); std::error_code error; + result.reserve( result.size() + countItemsInPath( basePath ) ); for ( auto iterator = fs::recursive_directory_iterator{ basePath, fs::directory_options::skip_permission_denied, error }; iterator != fs::recursive_directory_iterator{}; ++iterator ) { From 2441b8ab3dc4186679bccb7b7a3a230dc79adbac Mon Sep 17 00:00:00 2001 From: Oz Date: Fri, 16 Aug 2024 18:48:18 +0200 Subject: [PATCH 16/18] Improve documentation comments --- include/bit7z/bitoutputarchive.hpp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/include/bit7z/bitoutputarchive.hpp b/include/bit7z/bitoutputarchive.hpp index db14eaba..a9175d41 100644 --- a/include/bit7z/bitoutputarchive.hpp +++ b/include/bit7z/bitoutputarchive.hpp @@ -193,7 +193,11 @@ class BitOutputArchive { void addDirectory( const tstring& inDir ); /** - * @brief Adds the contents of the given directory path that match the given wildcard filter. + * @brief Adds the contents of the given directory path. + * + * This function iterates through the specified directory and adds its contents + * based on the provided wildcard filter. Optionally, the operation can be + * recursive, meaning it will include subdirectories and their contents. * * @param inDir the directory where to search for files to be added to the output archive. * @param filter the wildcard filter to be used for searching the files. @@ -202,7 +206,11 @@ class BitOutputArchive { void addDirectoryContents( const tstring& inDir, const tstring& filter, bool recursive ); /** - * @brief Adds the contents of the given directory path that match the given wildcard filter. + * @brief Adds the contents of the given directory path. + * + * This function iterates through the specified directory and adds its contents + * based on the provided wildcard filter and policy. Optionally, the operation can be + * recursive, meaning it will include subdirectories and their contents. * * @param inDir the directory where to search for files to be added to the output archive. * @param filter (optional) the wildcard filter to be used for searching the files. From d7acc33d5f912ad22882c94b946e3e87f7796b53 Mon Sep 17 00:00:00 2001 From: Oz Date: Fri, 16 Aug 2024 19:14:47 +0200 Subject: [PATCH 17/18] Fix clang-tidy warnings --- src/internal/fsindexer.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/internal/fsindexer.cpp b/src/internal/fsindexer.cpp index c5eb8cc9..b81c722d 100644 --- a/src/internal/fsindexer.cpp +++ b/src/internal/fsindexer.cpp @@ -34,11 +34,13 @@ FilesystemIndexer::FilesystemIndexer( FilesystemItem directory, } } +namespace { inline auto countItemsInPath( const fs::path& path ) -> std::size_t { std::error_code error; auto begin = fs::recursive_directory_iterator{ path, fs::directory_options::skip_permission_denied, error }; return error ? 0 : static_cast< std::size_t >( std::distance( begin, fs::recursive_directory_iterator{} ) ); } +} // namespace // NOTE: It indexes all the items whose metadata are needed in the archive to be created! void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< GenericInputItem > >& result, @@ -48,7 +50,7 @@ void FilesystemIndexer::listDirectoryItems( std::vector< std::unique_ptr< Generi mDirItem.inArchivePath().filename() != mDirItem.filesystemName(); const bool shouldIncludeMatchedItems = mPolicy == FilterPolicy::Include; - fs::path basePath = mDirItem.filesystemPath(); + const fs::path basePath = mDirItem.filesystemPath(); std::error_code error; result.reserve( result.size() + countItemsInPath( basePath ) ); for ( auto iterator = fs::recursive_directory_iterator{ basePath, fs::directory_options::skip_permission_denied, error }; From 07d7ba489caf5653b43af877909ba4db80d3a286 Mon Sep 17 00:00:00 2001 From: Oz Date: Fri, 16 Aug 2024 19:22:04 +0200 Subject: [PATCH 18/18] Fix code formatting --- src/bitfilecompressor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bitfilecompressor.cpp b/src/bitfilecompressor.cpp index fe0359f6..3f2110a8 100644 --- a/src/bitfilecompressor.cpp +++ b/src/bitfilecompressor.cpp @@ -73,7 +73,7 @@ void BitFileCompressor::compressDirectory( const tstring& inDir, const tstring& void BitFileCompressor::compressDirectoryContents( const tstring& inDir, const tstring& outFile, bool recursive, - const tstring& filter) const { + const tstring& filter ) const { if ( !compressionFormat().hasFeature( FormatFeatures::MultipleFiles ) ) { throw BitException( "Cannot compress multiple files", make_error_code( BitError::UnsupportedOperation ) ); }