1 /** @addtogroup binary_io
2 * @{
3 */
4 /*
5 Copyright (C) 2016 D Levin (https://www.kfrlib.com)
6 This file is part of KFR
7
8 KFR is free software: you can redistribute it and/or modify
9 it under the terms of the GNU General Public License as published by
10 the Free Software Foundation, either version 2 of the License, or
11 (at your option) any later version.
12
13 KFR is distributed in the hope that it will be useful,
14 but WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 GNU General Public License for more details.
17
18 You should have received a copy of the GNU General Public License
19 along with KFR.
20
21 If GPL is not suitable for your project, you must purchase a commercial license to use KFR.
22 Buying a commercial license is mandatory as soon as you develop commercial activities without
23 disclosing the source code of your own applications.
24 See https://www.kfrlib.com for details.
25 */
26 #pragma once
27
28 #include "../base/univector.hpp"
29 #include "../simd/impl/function.hpp"
30 #include "../simd/vec.hpp"
31 #include <cstdio>
32 #include <string>
33 #include <vector>
34
35 namespace kfr
36 {
37
38 #ifdef CMT_OS_WIN
39 using filepath_char = wchar_t;
40 #define KFR_FILEPATH_PREFIX_CONCAT(x, y) x##y
41 #define KFR_FILEPATH(s) KFR_FILEPATH_PREFIX_CONCAT(L, s)
42 #else
43 using filepath_char = char;
44 #define KFR_FILEPATH(s) s
45 #endif
46
47 using filepath = std::basic_string<filepath_char>;
48
49 #if defined _MSC_VER // MSVC
50 #define IO_SEEK_64 _fseeki64
51 #define IO_TELL_64 _ftelli64
52 #elif defined _WIN32 // MingW
53 #define IO_SEEK_64 fseeko64
54 #define IO_TELL_64 ftello64
55 #else // macOS, Linux
56 #define IO_SEEK_64 fseeko
57 #define IO_TELL_64 ftello
58 #endif
59
60 /// @brief Opens file using portable path (char* on posix, wchar_t* on windows)
fopen_portable(const filepath_char * path,const filepath_char * mode)61 inline FILE* fopen_portable(const filepath_char* path, const filepath_char* mode)
62 {
63 #ifdef CMT_OS_WIN
64 FILE* f = nullptr;
65 errno_t e = _wfopen_s(&f, path, mode);
66 (void)e;
67 return f;
68 #else
69 return fopen(path, mode);
70 #endif
71 }
72
73 template <typename T = void>
element_size()74 constexpr inline size_t element_size()
75 {
76 return sizeof(T);
77 }
78 template <>
element_size()79 constexpr inline size_t element_size<void>()
80 {
81 return 1;
82 }
83
84 /// @brief Seek origin
85 enum class seek_origin : int
86 {
87 current = SEEK_CUR, ///< From the current position
88 begin = SEEK_SET, ///< From the beginning
89 end = SEEK_END, ///< From the end
90 };
91
92 /// @brief Base class for all typed readers and writer
93 template <typename T = void>
94 struct abstract_stream
95 {
~abstract_streamkfr::abstract_stream96 virtual ~abstract_stream() {}
97 virtual imax tell() const = 0;
98 virtual bool seek(imax offset, seek_origin origin) = 0;
seekkfr::abstract_stream99 bool seek(imax offset, int origin) { return seek(offset, static_cast<seek_origin>(origin)); }
100 };
101
102 namespace internal_generic
103 {
104 struct empty
105 {
106 };
107
108 } // namespace internal_generic
109
110 /// @brief Base class for all typed readers
111 template <typename T = void>
112 struct abstract_reader : abstract_stream<T>
113 {
114 virtual size_t read(T* data, size_t size) = 0;
115
116 template <univector_tag Tag>
readkfr::abstract_reader117 size_t read(univector<T, Tag>& data)
118 {
119 return read(data.data(), data.size());
120 }
readkfr::abstract_reader121 size_t read(univector_ref<T>&& data) { return read(data.data(), data.size()); }
122
readkfr::abstract_reader123 univector<T> read(size_t size)
124 {
125 univector<T> result(size);
126 this->read(result);
127 return result;
128 }
readkfr::abstract_reader129 bool read(conditional<is_void<T>, internal_generic::empty, T>& data) { return read(&data, 1) == 1; }
130 };
131
132 /// @brief Base class for all typed writers
133 template <typename T = void>
134 struct abstract_writer : abstract_stream<T>
135 {
136 virtual size_t write(const T* data, size_t size) = 0;
137
138 template <univector_tag Tag>
writekfr::abstract_writer139 size_t write(const univector<T, Tag>& data)
140 {
141 return write(data.data(), data.size());
142 }
writekfr::abstract_writer143 size_t write(univector_ref<const T>&& data) { return write(data.data(), data.size()); }
writekfr::abstract_writer144 bool write(const conditional<is_void<T>, internal_generic::empty, T>& data)
145 {
146 return write(&data, 1) == 1;
147 }
148 };
149
150 template <typename From, typename To = void>
151 struct reader_adapter : abstract_reader<To>
152 {
153 static_assert(element_size<From>() % element_size<To>() == 0 ||
154 element_size<To>() % element_size<From>() == 0,
155 "From and To sizes must be compatible");
reader_adapterkfr::reader_adapter156 reader_adapter(std::shared_ptr<abstract_reader<From>>&& reader) : reader(std::move(reader)) {}
readkfr::reader_adapter157 virtual size_t read(To* data, size_t size) final
158 {
159 return reader->read(reinterpret_cast<From*>(data), size * element_size<From>() / element_size<To>()) *
160 element_size<To>() / element_size<From>();
161 }
162 std::shared_ptr<abstract_reader<From>> reader;
163 };
164
165 template <typename From, typename To = void>
166 struct writer_adapter : abstract_writer<To>
167 {
168 static_assert(element_size<From>() % element_size<To>() == 0 ||
169 element_size<To>() % element_size<From>() == 0,
170 "From and To sizes must be compatible");
writer_adapterkfr::writer_adapter171 writer_adapter(std::shared_ptr<abstract_writer<From>>&& writer) : writer(std::move(writer)) {}
writekfr::writer_adapter172 virtual size_t write(const To* data, size_t size) final
173 {
174 return writer->write(reinterpret_cast<const From*>(data),
175 size * element_size<From>() / element_size<To>()) *
176 element_size<To>() / element_size<From>();
177 }
178 std::shared_ptr<abstract_writer<From>> writer;
179 };
180
181 /// @brief Binary reader
182 using binary_reader = abstract_reader<>;
183
184 /// @brief Binary writer
185 using binary_writer = abstract_writer<>;
186
187 /// @brief Byte reader
188 using byte_reader = abstract_reader<u8>;
189
190 /// @brief Byte writer
191 using byte_writer = abstract_writer<u8>;
192
193 /// @brief float reader
194 using f32_reader = abstract_reader<f32>;
195
196 /// @brief float writer
197 using f32_writer = abstract_writer<f32>;
198
199 struct file_handle
200 {
file_handlekfr::file_handle201 file_handle(FILE* file) : file(file) {}
202 file_handle() = delete;
203 file_handle(const file_handle&) = delete;
file_handlekfr::file_handle204 file_handle(file_handle&& handle) : file(nullptr) { swap(handle); }
~file_handlekfr::file_handle205 ~file_handle()
206 {
207 if (file)
208 {
209 fclose(file);
210 }
211 }
212 FILE* file;
swapkfr::file_handle213 void swap(file_handle& handle) { std::swap(file, handle.file); }
214 };
215
216 /// @brief Typed file reader
217 template <typename T = void>
218 struct file_reader : abstract_reader<T>
219 {
file_readerkfr::file_reader220 file_reader(file_handle&& handle) : handle(std::move(handle)) {}
~file_readerkfr::file_reader221 ~file_reader() override {}
readkfr::file_reader222 size_t read(T* data, size_t size) final { return fread(data, element_size<T>(), size, handle.file); }
223
224 using abstract_reader<T>::read;
225
tellkfr::file_reader226 imax tell() const final { return IO_TELL_64(handle.file); }
seekkfr::file_reader227 bool seek(imax offset, seek_origin origin) final
228 {
229 return !IO_SEEK_64(handle.file, offset, static_cast<int>(origin));
230 }
231 file_handle handle;
232 };
233
234 /// @brief Typed file writer
235 template <typename T = void>
236 struct file_writer : abstract_writer<T>
237 {
file_writerkfr::file_writer238 file_writer(file_handle&& handle) : handle(std::move(handle)) {}
~file_writerkfr::file_writer239 ~file_writer() override {}
240
241 using abstract_writer<T>::write;
writekfr::file_writer242 size_t write(const T* data, size_t size) final
243 {
244 return fwrite(data, element_size<T>(), size, handle.file);
245 }
tellkfr::file_writer246 imax tell() const final { return IO_TELL_64(handle.file); }
seekkfr::file_writer247 bool seek(imax offset, seek_origin origin) final
248 {
249 return !IO_SEEK_64(handle.file, offset, static_cast<int>(origin));
250 }
251 file_handle handle;
252 };
253
254 /// @brief Opens typed file for reading
255 template <typename T = void>
open_file_for_reading(const filepath & path)256 inline std::shared_ptr<file_reader<T>> open_file_for_reading(const filepath& path)
257 {
258 return std::make_shared<file_reader<T>>(fopen_portable(path.c_str(), KFR_FILEPATH("rb")));
259 }
260
261 /// @brief Opens typed file for writing
262 template <typename T = void>
open_file_for_writing(const filepath & path)263 inline std::shared_ptr<file_writer<T>> open_file_for_writing(const filepath& path)
264 {
265 return std::make_shared<file_writer<T>>(fopen_portable(path.c_str(), KFR_FILEPATH("wb")));
266 }
267
268 /// @brief Opens typed file for appending
269 template <typename T = void>
open_file_for_appending(const filepath & path)270 inline std::shared_ptr<file_writer<T>> open_file_for_appending(const filepath& path)
271 {
272 return std::make_shared<file_writer<T>>(fopen_portable(path.c_str(), KFR_FILEPATH("ab")));
273 }
274
275 #ifdef CMT_OS_WIN
276 /// @brief Opens typed file for reading
277 template <typename T = void>
open_file_for_reading(const std::string & path)278 inline std::shared_ptr<file_reader<T>> open_file_for_reading(const std::string& path)
279 {
280 return std::make_shared<file_reader<T>>(fopen(path.c_str(), "rb"));
281 }
282
283 /// @brief Opens typed file for writing
284 template <typename T = void>
open_file_for_writing(const std::string & path)285 inline std::shared_ptr<file_writer<T>> open_file_for_writing(const std::string& path)
286 {
287 return std::make_shared<file_writer<T>>(fopen(path.c_str(), "wb"));
288 }
289
290 /// @brief Opens typed file for appending
291 template <typename T = void>
open_file_for_appending(const std::string & path)292 inline std::shared_ptr<file_writer<T>> open_file_for_appending(const std::string& path)
293 {
294 return std::make_shared<file_writer<T>>(fopen(path.c_str(), "ab"));
295 }
296 #endif
297
298 } // namespace kfr
299