Skip to content

Commit

Permalink
Improve file handle abstraction
Browse files Browse the repository at this point in the history
  • Loading branch information
rikyoz committed Aug 4, 2024
1 parent f9fc635 commit 0d8ad40
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 68 deletions.
14 changes: 2 additions & 12 deletions include/bit7z/bitformat.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,6 @@
#include "bitdefines.hpp"
#include "bittypes.hpp"

#include <type_traits>

namespace bit7z {

/**
Expand All @@ -30,21 +28,13 @@ enum struct FormatFeatures : unsigned {
MultipleMethods = 1u << 5 ///< The format can use different compression methods.
};

template< typename Enum >
using underlying_type_t = typename std::underlying_type< Enum >::type;

template< typename Enum >
inline constexpr auto to_underlying( Enum enum_value ) noexcept -> underlying_type_t< Enum > {
return static_cast< underlying_type_t< Enum > >( enum_value );
}

inline constexpr auto operator|( FormatFeatures lhs, FormatFeatures rhs ) noexcept -> FormatFeatures {
constexpr auto operator|( FormatFeatures lhs, FormatFeatures rhs ) noexcept -> FormatFeatures {
return static_cast< FormatFeatures >( to_underlying( lhs ) | to_underlying( rhs ) );
}

using FormatFeaturesType = underlying_type_t< FormatFeatures >;

inline constexpr auto operator&( FormatFeatures lhs, FormatFeatures rhs ) noexcept -> FormatFeaturesType {
constexpr auto operator&( FormatFeatures lhs, FormatFeatures rhs ) noexcept -> FormatFeaturesType {
return to_underlying( lhs ) & to_underlying( rhs );
}

Expand Down
8 changes: 8 additions & 0 deletions include/bit7z/bittypes.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,14 @@ auto to_tstring( const native_string& str ) -> tstring;
auto to_tstring( const native_string& str ) -> const tstring&;
#endif

template< typename Enum >
using underlying_type_t = typename std::underlying_type< Enum >::type;

template< typename Enum >
constexpr auto to_underlying( Enum enum_value ) noexcept -> underlying_type_t< Enum > {
return static_cast< underlying_type_t< Enum > >( enum_value );
}

} // namespace bit7z

#endif // BITTYPES_HPP
82 changes: 28 additions & 54 deletions src/internal/filehandle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@
#include "bitexception.hpp"
#include "internal/stringutil.hpp"

#include <fcntl.h>
#ifdef _WIN32
#include <io.h>

Expand Down Expand Up @@ -47,7 +46,27 @@ inline auto errno_as_hresult() -> HRESULT {
#endif
}

FileHandle::FileHandle( const handle_t handle ) : mHandle{ handle } {}
inline auto open_file( const fs::path& filePath, OpenFlags openFlags, PermissionFlag permissionFlag ) -> handle_t {
#ifdef _WIN32
handle_t handle = -1;
const errno_t result = _wsopen_s( &handle,
filePath.c_str(),
to_underlying( openFlags ),
_SH_DENYNO,
to_underlying( permissionFlag ) );
if ( result != 0 ) {
#else
// NOLINTNEXTLINE(*-vararg)
handle_t handle = open( filePath.c_str(), to_underlying( openFlags ), to_underlying( permissionFlag ) );
if ( handle < 0 ) {
#endif
throw BitException( "Could not open the file", make_hresult_code( errno_as_hresult() ) );
}
return handle;
}

FileHandle::FileHandle( const fs::path& filePath, OpenFlags openFlags, PermissionFlag permissionFlag )
: mHandle{open_file( filePath, openFlags, permissionFlag ) } {}

FileHandle::~FileHandle() {
close( mHandle );
Expand All @@ -64,38 +83,10 @@ auto FileHandle::seek( SeekOrigin origin, const Int64 distance, UInt64* newPosit
return S_OK;
}

inline auto openOutputFile( const fs::path& filePath, const bool createAlways ) -> handle_t {
std::error_code error;
if ( !createAlways && fs::exists( filePath, 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( filePath ) );
}

#ifdef _WIN32
handle_t handle = -1;
const errno_t result = _wsopen_s( &handle,
filePath.c_str(),
_O_WRONLY | _O_CREAT | _O_TRUNC | _O_BINARY,
_SH_DENYNO,
_S_IREAD | _S_IWRITE );
if ( result != 0 ) {
const auto errorValue = HRESULT_FROM_WIN32( _doserrno );
throw BitException( "Could not open the input file", make_hresult_code( errorValue ) );
}
#else
handle_t handle = open( filePath.c_str(), O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR ); // NOLINT(*-vararg)
if ( handle < 0 ) {
throw BitException( "Could not open the output file", last_error_code() );
}
#endif
return handle;
}

OutputFile::OutputFile( const fs::path& filePath, const bool createAlways )
: FileHandle{ openOutputFile( filePath, createAlways ) } {}
: FileHandle{ filePath,
AccessFlag::WriteOnly | ( createAlways ? FileFlag::CreateAlways : FileFlag::CreateNew ),
PermissionFlag::ReadWrite } {}

auto OutputFile::write( const void* data, const UInt32 size, UInt32* processedSize ) const noexcept -> HRESULT {
const auto result = ::write( mHandle, data, size );
Expand All @@ -111,28 +102,11 @@ auto OutputFile::write( const void* data, const UInt32 size, UInt32* processedSi
return S_OK;
}

inline auto openInputFile( const fs::path& filePath ) -> handle_t {
#ifdef _WIN32
handle_t handle = -1;
const errno_t result = _wsopen_s( &handle,
filePath.c_str(),
_O_RDONLY | _O_BINARY,
_SH_DENYNO,
_S_IREAD );
if ( result != 0 ) {
const auto error = HRESULT_FROM_WIN32( _doserrno );
throw BitException( "Could not open the input file", make_hresult_code( error ) );
}
#else
handle_t handle = open( filePath.c_str(), O_RDONLY ); // NOLINT(*-vararg)
if ( handle < 0 ) {
throw BitException( "Could not open the input file", last_error_code() );
}
#endif
return handle;
}
// Guaranteeing that the input file open flags are calculated at compile time.
constexpr auto openInputFlags = AccessFlag::ReadOnly | FileFlag::Existing;

InputFile::InputFile( const fs::path& filePath ) : FileHandle{ openInputFile( filePath ) } {}
InputFile::InputFile( const fs::path& filePath )
: FileHandle{ filePath, openInputFlags, PermissionFlag::Read } {}

auto InputFile::read( void* data, const UInt32 size, UInt32* processedSize ) const noexcept -> HRESULT {
const auto result = ::read( mHandle, data, size );
Expand Down
79 changes: 77 additions & 2 deletions src/internal/filehandle.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,29 +10,104 @@
#ifndef FILEHANDLE_HPP
#define FILEHANDLE_HPP

#include "bittypes.hpp" // For to_underlying
#include "bitwindows.hpp" // For HRESULT
#include "internal/fs.hpp"

#include <Common/MyTypes.h> // For 7-Zip integer types

#include <cstdint>
#include <cstdio>
#include <fcntl.h>

namespace bit7z {

using handle_t = int;

enum class SeekOrigin : std::uint8_t {
enum struct SeekOrigin : std::int8_t {
/** Set the file pointer at the given offset from the beginning of the file. */
Begin = SEEK_SET,

/** Set the file pointer at the given offset from the current file pointer position. */
CurrentPosition = SEEK_CUR,

/** Set the file pointer at the given offset from the end of the file. */
End = SEEK_END
};

#ifdef _WIN32
#define O_FLAG(flag) _O_##flag

#ifndef S_IRUSR
constexpr std::uint16_t S_IRUSR = _S_IREAD;
#endif
#ifndef S_IWUSR
constexpr std::uint16_t S_IWUSR = _S_IWRITE;
#endif
#else
#define O_FLAG(flag) O_##flag

constexpr std::uint16_t O_BINARY = 0;
#endif

enum struct AccessFlag : std::uint8_t {
/** Open a file only for reading. */
ReadOnly = O_FLAG( RDONLY ),

/** Open a file only for writing. */
WriteOnly = O_FLAG( WRONLY ),

/** Open a file for both reading and writing. */
ReadWrite = O_FLAG( RDWR )
};

enum struct FileFlag : std::uint16_t {
/** Open a file only if it already exists, fail otherwise. */
Existing = O_FLAG( BINARY ),

/** Open a file only if it already exists, truncating it to zero bytes. */
TruncateExisting = O_FLAG( TRUNC ) | O_FLAG( BINARY ),

/** Open a file only if it already exists, setting the file pointer to the end of the file. */
AppendExisting = O_FLAG( APPEND ) | O_FLAG( BINARY ),

/** Create a new file if it doesn't exist, fail otherwise. */
CreateNew = O_FLAG( CREAT ) | O_FLAG( EXCL ) | O_FLAG( BINARY ),

/** Open a file, creating it if it doesn't exist. */
OpenAlways = O_FLAG( CREAT ) | O_FLAG( BINARY ),

/** Open a file, creating it if it doesn't exist, truncating it if it already exists. */
CreateAlways = O_FLAG( CREAT ) | O_FLAG( TRUNC ) | O_FLAG( BINARY ),

/** Open a file, creating it if it doesn't exist, setting the file pointer to the end of the file. */
AppendAlways = O_FLAG( CREAT ) | O_FLAG( APPEND ) | O_FLAG( BINARY ),
};

/** Strong typedef for the POSIX open flags. */
enum struct OpenFlags : std::uint16_t {};

/** Open flags can be created only by concatenating access and file flags. */
constexpr auto operator|( AccessFlag accessFlag, FileFlag fileFlag ) -> OpenFlags {
return static_cast< OpenFlags >( to_underlying( accessFlag ) | to_underlying( fileFlag ) );
}

enum struct PermissionFlag : std::uint16_t {
/** The user will be able to read the file. */
Read = S_IRUSR,

/** The user will be able to write to the file. */
Write = S_IWUSR,

/** The user will be able to read and write the file. */
ReadWrite = S_IRUSR | S_IWUSR
};

class FileHandle {
protected:
handle_t mHandle{ -1 };

explicit FileHandle( handle_t handle );
explicit FileHandle( const fs::path& filePath, OpenFlags openFlags, PermissionFlag permissionFlag );

public:
explicit FileHandle( const FileHandle& ) = delete;
Expand Down

0 comments on commit 0d8ad40

Please sign in to comment.