Skip to content

Commit

Permalink
Re-implement file streams using low-level IO posix functions
Browse files Browse the repository at this point in the history
  • Loading branch information
rikyoz committed Aug 2, 2024
1 parent 04d7105 commit 27abf0c
Show file tree
Hide file tree
Showing 13 changed files with 311 additions and 84 deletions.
2 changes: 2 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ set( HEADERS
src/internal/extractcallback.hpp
src/internal/failuresourcecategory.hpp
src/internal/fileextractcallback.hpp
src/internal/filehandle.cpp
src/internal/fixedbufferextractcallback.hpp
src/internal/formatdetect.hpp
src/internal/fsindexer.hpp
Expand Down Expand Up @@ -155,6 +156,7 @@ set( SOURCES
src/internal/extractcallback.cpp
src/internal/failuresourcecategory.cpp
src/internal/fileextractcallback.cpp
src/internal/filehandle.cpp
src/internal/fixedbufferextractcallback.cpp
src/internal/formatdetect.cpp
src/internal/fsindexer.cpp
Expand Down
2 changes: 2 additions & 0 deletions cmake/CompilerOptions.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
# compiler-specific options

if( MSVC )
target_compile_definitions( ${LIB_TARGET} PRIVATE _CRT_DECLARE_NONSTDC_NAMES=0 )

# setting a pdb file name for debug builds (otherwise it is not generated!)
set_target_properties( ${LIB_TARGET} PROPERTIES COMPILE_PDB_NAME_DEBUG ${LIB_TARGET}${CMAKE_DEBUG_POSTFIX} )

Expand Down
7 changes: 5 additions & 2 deletions src/bitarchiveitem.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,9 +128,12 @@ auto BitArchiveItem::crc() const -> uint32_t {
}

// On MSVC, these macros are not defined, so we define them here.
#if !defined(S_ISLNK) && defined(S_IFMT)
#if !defined(S_ISLNK)
#ifndef S_IFLNK
constexpr auto S_IFLNK = 0xA000;
constexpr auto S_IFLNK = 0xA000;
#endif
#ifndef S_IFMT
constexpr auto S_IFMT = 0xF000;
#endif
#define S_ISLNK( m ) (((m) & S_IFMT) == S_IFLNK)
#endif
Expand Down
40 changes: 16 additions & 24 deletions src/internal/cfileinstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,34 +12,26 @@

#include "internal/cfileinstream.hpp"

#include "bitexception.hpp"
#include "internal/cstdinstream.hpp"
#include "internal/fs.hpp"
#include "internal/stringutil.hpp"
namespace bit7z {

#include <ios>
CFileInStream::CFileInStream( const fs::path& filePath ) : mFile{ filePath } {}

namespace bit7z {
COM_DECLSPEC_NOTHROW
STDMETHODIMP CFileInStream::Read( void* data, UInt32 size, UInt32* processedSize ) noexcept {
if ( processedSize != nullptr ) {
*processedSize = 0;
}

CFileInStream::CFileInStream( const fs::path& filePath ) : CStdInStream( mFileStream ) {
/* Disabling std::ifstream's buffering, as unbuffered IO gives better performance
* with the data block sizes read 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 );
mFileStream.rdbuf()->pubsetbuf( nullptr, 0 );
if ( size == 0 ) {
return S_OK;
}

return mFile.read( data, size, processedSize );
}

void CFileInStream::openFile( const fs::path& filePath ) {
mFileStream.open( filePath, std::ios::in | std::ios::binary ); // flawfinder: ignore
if ( mFileStream.fail() ) {
#if defined( __MINGW32__ ) || defined( __MINGW64__ )
std::error_code error{ errno, std::generic_category() };
throw BitException( "Failed to open the archive file", error, path_to_tstring( filePath ) );
#else
throw BitException( "Failed to open the archive file", last_error_code(), path_to_tstring( filePath ) );
#endif
}
COM_DECLSPEC_NOTHROW
STDMETHODIMP CFileInStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept {
return mFile.seek( static_cast< SeekOrigin >( seekOrigin ), offset, newPosition );
}

} // namespace bit7z
} // namespace bit7z
30 changes: 25 additions & 5 deletions src/internal/cfileinstream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,41 @@
#ifndef CFILEINSTREAM_HPP
#define CFILEINSTREAM_HPP

#include "internal/cstdinstream.hpp"
#include "bitexception.hpp"
#include "internal/com.hpp"
#include "internal/filehandle.hpp"
#include "internal/fs.hpp"
#include "internal/guids.hpp"
#include "internal/macros.hpp"

#include <array>
#include <7zip/IStream.h>

namespace bit7z {

class CFileInStream : public CStdInStream {
class CFileInStream : public IInStream, public CMyUnknownImp {
public:
explicit CFileInStream( const fs::path& filePath );

void openFile( const fs::path& filePath );
CFileInStream( const CFileInStream& ) = delete;

CFileInStream( CFileInStream&& ) = delete;

auto operator=( const CFileInStream& ) -> CFileInStream& = delete;

auto operator=( CFileInStream&& ) -> CFileInStream& = delete;

MY_UNKNOWN_VIRTUAL_DESTRUCTOR( ~CFileInStream() ) = default;

// IInStream
BIT7Z_STDMETHOD( Read, void* data, UInt32 size, UInt32* processedSize );

BIT7Z_STDMETHOD( Seek, Int64 offset, UInt32 seekOrigin, UInt64* newPosition );

// NOLINTNEXTLINE(modernize-use-noexcept, modernize-use-trailing-return-type, readability-identifier-length)
MY_UNKNOWN_IMP1( IInStream ) //-V2507 //-V2511 //-V835 //-V3504

private:
fs::ifstream mFileStream;
InputFile mFile;
};

} // namespace bit7z
Expand Down
51 changes: 15 additions & 36 deletions src/internal/cfileoutstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,56 +12,35 @@

#include "internal/cfileoutstream.hpp"

#include "bitexception.hpp"
#include "internal/cstdoutstream.hpp"
#include "internal/stringutil.hpp"

#include <ios>
#include <system_error>
#include <utility>

namespace bit7z {

CFileOutStream::CFileOutStream( fs::path filePath, bool createAlways )
: CStdOutStream( mFileStream ), mFilePath{ std::move( filePath ) } {
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.
error = std::make_error_code( std::errc::file_exists );
}
throw BitException( "Failed to create the output file", error, path_to_tstring( mFilePath ) );
CFileOutStream::CFileOutStream( const fs::path& filePath, bool createAlways )
: mFile( filePath, createAlways ), mFilePath{ filePath } {}

COM_DECLSPEC_NOTHROW
STDMETHODIMP CFileOutStream::Write( const void* data, UInt32 size, UInt32* processedSize ) noexcept {
if ( processedSize != nullptr ) {
*processedSize = 0;
}

/* Disabling std::ofstream's buffering, as unbuffered IO gives better performance
* with the data block sizes 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__ )
error = std::error_code{ errno, std::generic_category() };
throw BitException( "Failed to open the output file", error, path_to_tstring( mFilePath ) );
#else
throw BitException( "Failed to open the output file", last_error_code(), path_to_tstring( mFilePath ) );
#endif
if ( size == 0 ) {
return S_OK;
}
mFileStream.rdbuf()->pubsetbuf( nullptr, 0 );

return mFile.write( data, size, processedSize );
}

auto CFileOutStream::fail() const -> bool {
return mFileStream.fail();
COM_DECLSPEC_NOTHROW
STDMETHODIMP CFileOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept {
return mFile.seek( static_cast< SeekOrigin >( seekOrigin ), offset, newPosition );
}


COM_DECLSPEC_NOTHROW
STDMETHODIMP CFileOutStream::SetSize( UInt64 newSize ) noexcept {
std::error_code error;
fs::resize_file( mFilePath, newSize, error );
return error ? E_FAIL : S_OK;
}

auto CFileOutStream::path() const -> const fs::path& {
return mFilePath;
}

} // namespace bit7z
33 changes: 24 additions & 9 deletions src/internal/cfileoutstream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,28 +10,43 @@
#ifndef CFILEOUTSTREAM_HPP
#define CFILEOUTSTREAM_HPP

#include "bitdefines.hpp"
#include "internal/cstdoutstream.hpp"
#include "internal/fs.hpp"
#include "bitexception.hpp"
#include "internal/com.hpp"
#include "internal/filehandle.hpp"
#include "internal/guids.hpp"
#include "internal/macros.hpp"

#include <array>
#include <7zip/IStream.h>

namespace bit7z {

class CFileOutStream : public CStdOutStream {
class CFileOutStream : public IOutStream, public CMyUnknownImp {
public:
explicit CFileOutStream( fs::path filePath, bool createAlways = false );
explicit CFileOutStream( const fs::path& filePath, bool createAlways = false );

BIT7Z_NODISCARD auto path() const -> const fs::path&;
CFileOutStream( const CFileOutStream& ) = delete;

BIT7Z_NODISCARD auto fail() const -> bool;
CFileOutStream( CFileOutStream&& ) = delete;

auto operator=( const CFileOutStream& ) -> CFileOutStream& = delete;

auto operator=( CFileOutStream&& ) -> CFileOutStream& = delete;

MY_UNKNOWN_VIRTUAL_DESTRUCTOR( ~CFileOutStream() ) = default;

// IOutStream
BIT7Z_STDMETHOD( Write, void const* data, UInt32 size, UInt32* processedSize );

BIT7Z_STDMETHOD( Seek, Int64 offset, UInt32 seekOrigin, UInt64* newPosition );

BIT7Z_STDMETHOD( SetSize, UInt64 newSize );

// NOLINTNEXTLINE(modernize-use-noexcept, modernize-use-trailing-return-type, readability-identifier-length)
MY_UNKNOWN_IMP1( IOutStream ) //-V2507 //-V2511 //-V835 //-V3504

private:
OutputFile mFile;
fs::path mFilePath;
fs::ofstream mFileStream;
};

} // namespace bit7z
Expand Down
2 changes: 1 addition & 1 deletion src/internal/cmultivolumeoutstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ STDMETHODIMP CMultiVolumeOutStream::SetSize( UInt64 newSize ) noexcept {
newSize -= volume->currentSize();
}
while ( !mVolumes.empty() ) {
const fs::path volumePath = mVolumes.back()->path();
const fs::path volumePath = mVolumes.back()->volumePath();
mVolumes.pop_back();
std::error_code error;
fs::remove( volumePath, error );
Expand Down
10 changes: 7 additions & 3 deletions src/internal/cvolumeoutstream.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@
namespace bit7z {

CVolumeOutStream::CVolumeOutStream( const fs::path& volumeName )
: CFileOutStream( volumeName ), mCurrentOffset{ 0 }, mCurrentSize{ 0 } {}
: CFileOutStream( volumeName ), mCurrentOffset{ 0 }, mCurrentSize{ 0 }, mVolumePath{ volumeName } {}

COM_DECLSPEC_NOTHROW
STDMETHODIMP CVolumeOutStream::Seek( Int64 offset, UInt32 seekOrigin, UInt64* newPosition ) noexcept {
UInt64 pos{};
RINOK( CStdOutStream::Seek( offset, seekOrigin, &pos ) ) //-V3504
RINOK( CFileOutStream::Seek( offset, seekOrigin, &pos ) ) //-V3504
mCurrentOffset = pos;
if ( newPosition != nullptr ) {
*newPosition = pos;
Expand All @@ -38,7 +38,7 @@ STDMETHODIMP CVolumeOutStream::Write( const void* data, UInt32 size, UInt32* pro
}

UInt32 writtenSize{};
RINOK( CStdOutStream::Write( data, size, &writtenSize ) ) //-V3504
RINOK( CFileOutStream::Write( data, size, &writtenSize ) ) //-V3504

if ( writtenSize == 0 && size != 0 ) {
return E_FAIL;
Expand Down Expand Up @@ -70,4 +70,8 @@ void CVolumeOutStream::setCurrentSize( uint64_t currentSize ) {
mCurrentSize = currentSize;
}

auto CVolumeOutStream::volumePath() const -> const fs::path& {
return mVolumePath;
}

} // namespace bit7z
4 changes: 4 additions & 0 deletions src/internal/cvolumeoutstream.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ class CVolumeOutStream final : public CFileOutStream {

BIT7Z_NODISCARD auto currentSize() const -> uint64_t;

BIT7Z_NODISCARD auto volumePath() const -> const fs::path&;

void setCurrentSize( uint64_t currentSize );

// IOutStream
Expand All @@ -39,6 +41,8 @@ class CVolumeOutStream final : public CFileOutStream {
uint64_t mCurrentOffset;

uint64_t mCurrentSize;

fs::path mVolumePath;
};

} // namespace bit7z
Expand Down
4 changes: 0 additions & 4 deletions src/internal/fileextractcallback.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,6 @@ auto FileExtractCallback::finishOperation( OperationResult operationResult ) ->
return result;
}

if ( mFileOutStream->fail() ) {
return E_FAIL;
}

mFileOutStream.Release(); // We need to release the file to change its modified time.

if ( extractMode() != ExtractMode::Extract ) { // No need to set attributes or modified time of the file.
Expand Down
Loading

0 comments on commit 27abf0c

Please sign in to comment.