PixelBullet  0.0.1
A C++ game engine
Loading...
Searching...
No Matches
binary_file_io_internal.h
1#pragma once
2
3#include "pixelbullet/filesystem/native_path.h"
4
5#include <filesystem>
6#include <fstream>
7#include <limits>
8#include <span>
9#include <string>
10#include <string_view>
11#include <system_error>
12#include <type_traits>
13#include <utility>
14
15namespace pixelbullet::serialization_internal
16{
17inline void set_binary_io_error(std::string* error_message, std::string message)
18{
19 if (error_message != nullptr)
20 {
21 *error_message = std::move(message);
22 }
23}
24
25[[nodiscard]] inline bool streamsize_fits(const std::size_t byte_count) noexcept
26{
27 return byte_count <= static_cast<std::size_t>(std::numeric_limits<std::streamsize>::max());
28}
29
30[[nodiscard]] inline std::ifstream open_binary_input_file(const std::filesystem::path& path, const std::string_view label,
31 std::string* error_message = nullptr)
32{
33 if (path.empty())
34 {
35 set_binary_io_error(error_message, "Failed to open " + std::string(label) + " for reading: path is empty.");
36 return {};
37 }
38
39 std::ifstream stream(filesystem::to_native_file_io_path(path), std::ios::binary);
40 if (!stream)
41 {
42 set_binary_io_error(error_message, "Failed to open " + std::string(label) + " for reading: " + path.string());
43 }
44 return stream;
45}
46
47[[nodiscard]] inline std::ofstream open_binary_output_file(const std::filesystem::path& path, const std::string_view label,
48 std::string* error_message = nullptr)
49{
50 if (path.empty())
51 {
52 set_binary_io_error(error_message, "Failed to open " + std::string(label) + " for writing: path is empty.");
53 return {};
54 }
55
56 const std::filesystem::path native_path = filesystem::to_native_file_io_path(path);
57 const std::filesystem::path parent = native_path.parent_path();
58 if (!parent.empty())
59 {
60 std::error_code error_code;
61 std::filesystem::create_directories(parent, error_code);
62 if (error_code)
63 {
64 set_binary_io_error(error_message, "Failed to create " + std::string(label) + " output directory '" + parent.string() +
65 "': " + error_code.message());
66 return {};
67 }
68 }
69
70 std::ofstream stream(native_path, std::ios::binary | std::ios::trunc);
71 if (!stream)
72 {
73 set_binary_io_error(error_message, "Failed to open " + std::string(label) + " for writing: " + path.string());
74 }
75 return stream;
76}
77
78[[nodiscard]] inline bool finish_binary_output_file(std::ofstream& stream, const std::filesystem::path& path, const std::string_view label,
79 std::string* error_message = nullptr)
80{
81 stream.flush();
82 if (!stream)
83 {
84 set_binary_io_error(error_message, "Failed to flush " + std::string(label) + ": " + path.string());
85 return false;
86 }
87
88 stream.close();
89 if (!stream)
90 {
91 set_binary_io_error(error_message, "Failed to close " + std::string(label) + ": " + path.string());
92 return false;
93 }
94 return true;
95}
96
97[[nodiscard]] inline bool read_bytes(std::istream& stream, void* bytes, const std::size_t byte_count)
98{
99 if (byte_count == 0u)
100 {
101 return true;
102 }
103 if (bytes == nullptr || !streamsize_fits(byte_count))
104 {
105 return false;
106 }
107 return static_cast<bool>(stream.read(static_cast<char*>(bytes), static_cast<std::streamsize>(byte_count)));
108}
109
110[[nodiscard]] inline bool write_bytes(std::ostream& stream, const void* bytes, const std::size_t byte_count)
111{
112 if (byte_count == 0u)
113 {
114 return true;
115 }
116 if (bytes == nullptr || !streamsize_fits(byte_count))
117 {
118 return false;
119 }
120 return static_cast<bool>(stream.write(static_cast<const char*>(bytes), static_cast<std::streamsize>(byte_count)));
121}
122
123template <typename T>
124[[nodiscard]] bool read_trivial(std::istream& stream, T& value)
125{
126 static_assert(std::is_trivially_copyable_v<T>);
127 return read_bytes(stream, &value, sizeof(T));
128}
129
130template <typename T>
131[[nodiscard]] bool write_trivial(std::ostream& stream, const T& value)
132{
133 static_assert(std::is_trivially_copyable_v<T>);
134 return write_bytes(stream, &value, sizeof(T));
135}
136
137template <typename T>
138[[nodiscard]] bool read_trivial_span(std::istream& stream, const std::span<T> values)
139{
140 static_assert(std::is_trivially_copyable_v<T>);
141 return read_bytes(stream, values.data(), values.size_bytes());
142}
143
144template <typename T>
145[[nodiscard]] bool write_trivial_span(std::ostream& stream, const std::span<const T> values)
146{
147 static_assert(std::is_trivially_copyable_v<T>);
148 return write_bytes(stream, values.data(), values.size_bytes());
149}
150} // namespace pixelbullet::serialization_internal