Unravel Engine C++ Reference
Loading...
Searching...
No Matches
file_ostream.cpp
Go to the documentation of this file.
1#include "file_ostream.h"
2
3#include <algorithm>
4#include <array>
5#include <cstdio>
6#include <utility>
7
8namespace fs
9{
10namespace
11{
12
13class file_ostreambuf : public std::streambuf
14{
15public:
16 file_ostreambuf() = default;
17
18 file_ostreambuf(FILE* file, file_stream_ownership ownership)
19 : file_(file)
20 , owns_file_(ownership == file_stream_ownership::take_ownership)
21 {
22 reset_put_area();
23 }
24
25 ~file_ostreambuf() override
26 {
27 close_file();
28 }
29
30 file_ostreambuf(const file_ostreambuf&) = delete;
31 auto operator=(const file_ostreambuf&) -> file_ostreambuf& = delete;
32
33 file_ostreambuf(file_ostreambuf&& other) noexcept
34 : file_(other.file_)
35 , owns_file_(other.owns_file_)
36 , buffer_(other.buffer_)
37 {
38 other.file_ = nullptr;
39 other.owns_file_ = false;
40 other.setp(nullptr, nullptr);
41 reset_put_area();
42 }
43
44 auto open(const std::string& absolute_path, std::ios_base::openmode mode) -> bool
45 {
46 close_file();
47 mode |= std::ios_base::out;
48 char stdio_mode[4]{};
50 {
51 return false;
52 }
53 file_ = std::fopen(absolute_path.c_str(), stdio_mode);
54 owns_file_ = file_ != nullptr;
55 reset_put_area();
56 return file_ != nullptr;
57 }
58
59 auto attach(FILE* file, file_stream_ownership ownership) -> bool
60 {
61 close_file();
62 if(file == nullptr)
63 {
64 return false;
65 }
66 file_ = file;
67 owns_file_ = ownership == file_stream_ownership::take_ownership;
68 reset_put_area();
69 return true;
70 }
71
72 auto is_open() const -> bool
73 {
74 return file_ != nullptr;
75 }
76
77 auto native_handle() const -> FILE*
78 {
79 return file_;
80 }
81
82 void close()
83 {
84 sync_put_area();
85 close_file();
86 setp(nullptr, nullptr);
87 }
88
89 auto tell_file() const -> long long
90 {
91 if(file_ == nullptr)
92 {
93 return -1;
94 }
95 const long long file_pos = file_stream_detail::stdio_tell64(file_);
96 if(file_pos < 0)
97 {
98 return -1;
99 }
100 if(pptr() != nullptr && pbase() != nullptr && pptr() >= pbase())
101 {
102 return file_pos + static_cast<long long>(pptr() - pbase());
103 }
104 return file_pos;
105 }
106
107 auto seek_file(long long offset, int whence) -> bool
108 {
109 if(file_ == nullptr)
110 {
111 return false;
112 }
113 if(!sync_put_area())
114 {
115 return false;
116 }
117 if(whence == SEEK_END)
118 {
119 const long long size = file_stream_detail::stdio_file_size(file_);
120 if(size < 0)
121 {
122 return false;
123 }
124 offset = size + offset;
125 whence = SEEK_SET;
126 }
127 if(file_stream_detail::stdio_seek64(file_, offset, whence) != 0)
128 {
129 return false;
130 }
131 reset_put_area();
132 return true;
133 }
134
135 auto query_file_size() -> long long
136 {
137 if(file_ == nullptr)
138 {
139 return -1;
140 }
141 if(!sync_put_area())
142 {
143 return -1;
144 }
146 }
147
148 auto flush_file() -> bool
149 {
150 return sync_put_area() && file_ != nullptr && std::fflush(file_) == 0;
151 }
152
153protected:
154 auto overflow(int_type ch) -> int_type override
155 {
156 if(!sync_put_area())
157 {
158 return traits_type::eof();
159 }
160 if(ch != traits_type::eof())
161 {
162 const char value = traits_type::to_char_type(ch);
163 if(std::fwrite(&value, 1, 1, file_) != 1)
164 {
165 return traits_type::eof();
166 }
167 }
168 reset_put_area();
169 return ch;
170 }
171
172 auto xsputn(const char* src, std::streamsize count) -> std::streamsize override
173 {
174 if(count <= 0 || src == nullptr)
175 {
176 return 0;
177 }
178 std::streamsize written = 0;
179 while(written < count)
180 {
181 if(pptr() == epptr() && overflow(traits_type::to_int_type(src[written])) == traits_type::eof())
182 {
183 break;
184 }
185 const std::streamsize available = epptr() - pptr();
186 const std::streamsize chunk = std::min(count - written, available);
187 traits_type::copy(pptr(), src + written, static_cast<std::size_t>(chunk));
188 pbump(static_cast<int>(chunk));
189 written += chunk;
190 }
191 return written;
192 }
193
194 auto sync() -> int override
195 {
196 return sync_put_area() ? 0 : -1;
197 }
198
199 auto seekoff(std::streamoff off, std::ios_base::seekdir way, std::ios_base::openmode which)
200 -> std::streampos override
201 {
202 if((which & std::ios_base::out) == 0 || file_ == nullptr)
203 {
204 return std::streampos(std::streamoff(-1));
205 }
206 long long next_pos = 0;
207 switch(way)
208 {
209 case std::ios_base::beg:
210 next_pos = static_cast<long long>(off);
211 break;
212 case std::ios_base::cur:
213 next_pos = tell_file() + static_cast<long long>(off);
214 break;
215 case std::ios_base::end:
216 {
217 const long long size = query_file_size();
218 if(size < 0)
219 {
220 return std::streampos(std::streamoff(-1));
221 }
222 next_pos = size + static_cast<long long>(off);
223 break;
224 }
225 default:
226 return std::streampos(std::streamoff(-1));
227 }
228 if(next_pos < 0)
229 {
230 return std::streampos(std::streamoff(-1));
231 }
232 if(!seek_file(next_pos, SEEK_SET))
233 {
234 return std::streampos(std::streamoff(-1));
235 }
236 return std::streampos(static_cast<std::streamoff>(next_pos));
237 }
238
239 auto seekpos(std::streampos sp, std::ios_base::openmode which) -> std::streampos override
240 {
241 return seekoff(static_cast<std::streamoff>(sp), std::ios_base::beg, which);
242 }
243
244private:
245 void reset_put_area()
246 {
247 setp(buffer_.data(), buffer_.data() + static_cast<std::ptrdiff_t>(buffer_.size()));
248 }
249
250 auto sync_put_area() -> bool
251 {
252 if(file_ == nullptr)
253 {
254 return false;
255 }
256 if(pptr() == nullptr || pbase() == nullptr)
257 {
258 return true;
259 }
260 const std::size_t pending = static_cast<std::size_t>(pptr() - pbase());
261 if(pending == 0)
262 {
263 return true;
264 }
265 const std::size_t written = std::fwrite(pbase(), 1, pending, file_);
266 reset_put_area();
267 return written == pending;
268 }
269
270 void close_file()
271 {
272 if(file_ != nullptr && owns_file_)
273 {
274 std::fclose(file_);
275 }
276 file_ = nullptr;
277 owns_file_ = false;
278 }
279
280 FILE* file_ = nullptr;
281 bool owns_file_ = false;
282 std::array<char, file_stream_detail::default_buffer_size> buffer_{};
283};
284
285auto open_write_buffer(const std::string& absolute_path, std::ios_base::openmode mode)
286 -> std::unique_ptr<file_ostreambuf>
287{
288 auto buffer = std::make_unique<file_ostreambuf>();
289 if(!buffer->open(absolute_path, mode))
290 {
291 return nullptr;
292 }
293 return buffer;
294}
295
296} // namespace
297
299{
300 std::unique_ptr<file_ostreambuf> buffer;
301
302 explicit impl(std::unique_ptr<file_ostreambuf> stream_buffer)
303 : buffer(std::move(stream_buffer))
304 {
305 }
306};
307
309 : std::ostream(nullptr)
310 , impl_(std::make_unique<impl>(std::make_unique<file_ostreambuf>()))
311{
312 rdbuf(impl_->buffer.get());
313}
314
315file_ostream::file_ostream(const path& file_path, std::ios_base::openmode mode)
316 : file_ostream(file_path.string(), mode)
317{
318}
319
320file_ostream::file_ostream(const std::string& absolute_path, std::ios_base::openmode mode)
321 : std::ostream(nullptr)
322{
323 auto buffer = open_write_buffer(absolute_path, mode);
324 if(!buffer)
325 {
326 buffer = std::make_unique<file_ostreambuf>();
327 }
328 impl_ = std::make_unique<impl>(std::move(buffer));
329 rdbuf(impl_->buffer.get());
330 if(!is_open())
331 {
332 setstate(std::ios::failbit);
333 }
334}
335
336void file_ostream::open(const path& file_path, std::ios_base::openmode mode)
337{
338 open(file_path.string(), mode);
339}
340
341void file_ostream::open(const std::string& absolute_path, std::ios_base::openmode mode)
342{
343 if(!impl_)
344 {
345 impl_ = std::make_unique<impl>(std::make_unique<file_ostreambuf>());
346 rdbuf(impl_->buffer.get());
347 }
348 if(!impl_->buffer->open(absolute_path, mode))
349 {
350 setstate(std::ios::failbit);
351 return;
352 }
353 clear();
354}
355
357 : std::ostream(nullptr)
358 , impl_(std::make_unique<impl>(std::make_unique<file_ostreambuf>()))
359{
360 impl_->buffer->attach(file, ownership);
361 rdbuf(impl_->buffer.get());
362}
363
365 : std::ostream(nullptr)
366 , impl_(std::move(other.impl_))
367{
368 rdbuf(impl_ ? impl_->buffer.get() : nullptr);
369 other.rdbuf(nullptr);
370}
371
373{
374 if(this != &other)
375 {
376 impl_ = std::move(other.impl_);
377 rdbuf(impl_ ? impl_->buffer.get() : nullptr);
378 other.rdbuf(nullptr);
379 }
380 return *this;
381}
382
384
385auto file_ostream::is_open() const -> bool
386{
387 return impl_ && impl_->buffer && impl_->buffer->is_open();
388}
389
390auto file_ostream::native_handle() const -> FILE*
391{
392 return impl_ && impl_->buffer ? impl_->buffer->native_handle() : nullptr;
393}
394
395auto file_ostream::tell() const -> long long
396{
397 return impl_ && impl_->buffer ? impl_->buffer->tell_file() : -1;
398}
399
400auto file_ostream::file_size() const -> long long
401{
402 return impl_ && impl_->buffer ? impl_->buffer->query_file_size() : -1;
403}
404
405auto file_ostream::seek(long long offset, int whence) -> bool
406{
407 return impl_ && impl_->buffer && impl_->buffer->seek_file(offset, whence);
408}
409
411{
412 flush();
413 return impl_ && impl_->buffer && impl_->buffer->flush_file();
414}
415
417{
418 if(impl_ && impl_->buffer)
419 {
420 impl_->buffer->close();
421 }
422 clear();
423}
424
425} // namespace fs
stdio-backed output stream. Accepts std::ios open flags (e.g. std::ios::binary | std::ios::app)
auto file_size() const -> long long
~file_ostream() override
auto operator=(const file_ostream &) -> file_ostream &=delete
void open(const path &file_path, std::ios_base::openmode mode=default_file_write_mode)
auto is_open() const -> bool
auto seek(long long offset, int whence=SEEK_SET) -> bool
auto flush_file() -> bool
auto tell() const -> long long
auto native_handle() const -> FILE *
auto stdio_file_size(FILE *file) -> long long
auto stdio_tell64(FILE *file) -> long long
auto openmode_to_stdio_mode(std::ios_base::openmode mode, char(&out)[4]) -> bool
auto stdio_seek64(FILE *file, long long offset, int origin) -> int
Definition cache.hpp:11
file_stream_ownership
Whether a file stream wrapper should fclose its FILE* on destruction.
void close(const std::string &scope)
Closes the specified scope.
Definition seq.cpp:145
Hash specialization for batch_key to enable use in std::unordered_map.
uint32_t count
std::unique_ptr< file_ostreambuf > buffer
impl(std::unique_ptr< file_ostreambuf > stream_buffer)