Skip to content

Commit

Permalink
Merge branch 'hotfix/v4.0.8'
Browse files Browse the repository at this point in the history
  • Loading branch information
rikyoz committed Aug 16, 2024
2 parents f04d819 + 07d7ba4 commit bec6a22
Show file tree
Hide file tree
Showing 19 changed files with 428 additions and 64 deletions.
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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" )
Expand Down
15 changes: 15 additions & 0 deletions include/bit7z/bitfilecompressor.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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. */

/**
Expand Down
35 changes: 33 additions & 2 deletions include/bit7z/bitoutputarchive.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -186,12 +186,43 @@ 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 );

/**
* @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.
* @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.
*
* 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.
* @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.
*
Expand Down
2 changes: 1 addition & 1 deletion src/bitarchivereader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
12 changes: 12 additions & 0 deletions src/bitfilecompressor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
17 changes: 17 additions & 0 deletions src/bitoutputarchive.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand Down
13 changes: 6 additions & 7 deletions src/internal/cfileinstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand Down
2 changes: 0 additions & 2 deletions src/internal/cfileinstream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
10 changes: 7 additions & 3 deletions src/internal/cfileoutstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ) {
Expand All @@ -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__ )
Expand All @@ -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 {
Expand Down
3 changes: 0 additions & 3 deletions src/internal/cfileoutstream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
25 changes: 17 additions & 8 deletions src/internal/dateutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,31 +20,40 @@ 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;
}

#endif

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;
Expand Down
2 changes: 1 addition & 1 deletion src/internal/dateutil.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions src/internal/formatdetect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) |
Expand Down
56 changes: 32 additions & 24 deletions src/internal/fsindexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -32,45 +34,51 @@ 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!
// 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;

const 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;
}
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 ) {
const auto& currentEntry = *iterator;
const auto& itemPath = currentEntry.path();

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 ) );
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 ) );
}

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();
}
}
}
Expand Down
4 changes: 1 addition & 3 deletions src/internal/fsindexer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/internal/fsutil.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Loading

0 comments on commit bec6a22

Please sign in to comment.