Files
Z3D/include/util/binary_ifstream.hpp
2024-12-22 16:58:40 +01:00

208 lines
4.2 KiB
C++

#pragma once
#include <filesystem>
#include <fstream>
#include <span>
#include <algorithm>
#include <cstring>
class binary_ifstream {
public:
using char_type = char;
using size_type = std::streamsize;
[[nodiscard]] inline std::error_code open(
const std::filesystem::path& filename,
bool check_file_before_open
);
[[nodiscard]] inline std::error_code read(std::span<char_type> buffer);
template<std::endian E, std::integral T>
[[nodiscard]] std::error_code read(T& integer);
template<std::endian E, std::floating_point T>
[[nodiscard]] std::error_code read_ieee754(T& floating_point);
[[nodiscard]] inline std::error_code skip(size_type count);
template<std::integral T>
[[nodiscard]] std::error_code skip();
template<std::floating_point T>
[[nodiscard]] std::error_code skip();
inline void close();
protected:
[[nodiscard]] inline static std::error_code check_file(
const std::filesystem::path& filename
);
[[nodiscard]] inline std::errc get_stream_error_code() const;
private:
std::basic_ifstream<char_type> in{};
};
inline std::errc binary_ifstream::get_stream_error_code() const {
const auto state = in.rdstate();
auto code = std::errc{};
if (state != std::ios::goodbit)
{
code = std::errc::state_not_recoverable;
if (state & std::ios::failbit) // irrecoverable stream error
{
code = std::errc::state_not_recoverable;
}
else if (state & std::ios::badbit) // input/output operation failed
{
code = std::errc::io_error;
}
else if (state & std::ios::eofbit) // input sequence has reached end-of-file
{
code = std::errc::result_out_of_range;
}
}
return code;
}
inline std::error_code binary_ifstream::check_file(const std::filesystem::path& filename)
{
auto error = std::error_code{};
const auto is_file = std::filesystem::exists(filename, error);
if (error)
{
return error;
}
if (not is_file)
{
return std::make_error_code(std::errc::invalid_argument);
}
const auto files_exists = std::filesystem::exists(filename, error);
if (error)
{
return error;
}
if (not files_exists)
{
return std::make_error_code(std::errc::no_such_file_or_directory);
}
return {};
}
inline std::error_code binary_ifstream::open(
const std::filesystem::path& filename,
bool check_file_before_open
) {
if (check_file_before_open)
{
if (const auto error = check_file(filename))
{
return error;
}
}
in = {};
// Turn of exceptions, as errors are handled via the stream state.
in.exceptions(std::ios::goodbit);
in.open(filename, std::ios::binary | std::ios::in);
if (const auto error = std::make_error_code(get_stream_error_code()))
{
return error;
}
if (not in.is_open())
{
// Unknown error, so 'state_not_recoverable' is assumed.
return std::make_error_code(std::errc::state_not_recoverable);
}
return {};
}
inline void binary_ifstream::close()
{
in.close();
}
inline std::error_code binary_ifstream::skip(size_type count)
{
in.ignore(count);
return std::make_error_code(get_stream_error_code());
}
template<std::floating_point T>
std::error_code binary_ifstream::skip()
{
return skip(sizeof(T));
}
template<std::integral T>
std::error_code binary_ifstream::skip()
{
return skip(sizeof(T));
}
inline std::error_code binary_ifstream::read(std::span<char_type> buffer)
{
in.read(buffer.data(), static_cast<size_type>(buffer.size()));
return std::make_error_code(get_stream_error_code());
}
template<std::endian E, std::integral T>
std::error_code binary_ifstream::read(T& integer)
{
std::array<char_type, sizeof(T)> buffer;
if (const auto error = read(buffer))
{
return error;
}
if constexpr (std::endian::native != E)
{
std::reverse(buffer.begin(), buffer.end());
}
// Use memcpy to avoid UB.
std::memcpy(static_cast<void*>(&integer), buffer.data(), buffer.size());
return {};
}
template<std::endian E, std::floating_point T>
std::error_code binary_ifstream::read_ieee754(T& floating_point)
{
static_assert(std::numeric_limits<T>::is_iec559);
std::array<char_type, sizeof(T)> buffer;
if (const auto error = read(buffer))
{
return error;
}
if constexpr (std::endian::native != E)
{
std::reverse(buffer.begin(), buffer.end());
}
// Use memcpy to avoid UB.
std::memcpy(static_cast<void*>(&floating_point), buffer.data(), buffer.size());
return {};
}