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