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