source: mainline/uspace/lib/cpp/include/__bits/io/fstream.hpp@ 4248ce5

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 4248ce5 was 7dcce0a, checked in by jxsvoboda <5887334+jxsvoboda@…>, 6 years ago

cpp: abort and report when an unimplemented function is called

  • Property mode set to 100644
File size: 22.3 KB
Line 
1/*
2 * Copyright (c) 2019 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_BITS_IO_FSTREAM
30#define LIBCPP_BITS_IO_FSTREAM
31
32#include <cassert>
33#include <cstdio>
34#include <ios>
35#include <iosfwd>
36#include <locale>
37#include <streambuf>
38#include <string>
39
40namespace std
41{
42 /**
43 * 27.9.1.1, class template basic_filebuf:
44 */
45 template<class Char, class Traits>
46 class basic_filebuf: public basic_streambuf<Char, Traits>
47 {
48 public:
49 using char_type = Char;
50 using traits_type = Traits;
51 using int_type = typename traits_type::int_type;
52 using pos_type = typename traits_type::pos_type;
53 using off_type = typename traits_type::off_type;
54
55 /**
56 * 27.9.1.2, constructors/destructor:
57 */
58
59 basic_filebuf()
60 : basic_streambuf<char_type, traits_type>{},
61 obuf_{nullptr}, ibuf_{nullptr}, mode_{}, file_{nullptr}
62 { /* DUMMY BODY */ }
63
64 basic_filebuf(const basic_filebuf&) = delete;
65
66 basic_filebuf(basic_filebuf&& other)
67 : mode_{other.mode_}
68 {
69 close();
70 std::swap(obuf_, other.obuf_);
71 std::swap(ibuf_, other.ibuf_);
72 std::swap(file_, other.file_);
73
74 basic_streambuf<char_type, traits_type>::swap(other);
75 }
76
77 virtual ~basic_filebuf()
78 {
79 // TODO: exception here caught and not rethrown
80 close();
81 }
82
83 /**
84 * 27.9.1.3: assign/swap:
85 */
86
87 basic_filebuf& operator=(const basic_filebuf&) = delete;
88
89 basic_filebuf& operator=(basic_filebuf&& other)
90 {
91 close();
92 swap(other);
93
94 return *this;
95 }
96
97 void swap(basic_filebuf& rhs)
98 {
99 std::swap(mode_, rhs.mode_);
100 std::swap(obuf_, rhs.obuf_);
101 std::swap(ibuf_, rhs.ibuf_);
102
103 basic_streambuf<char_type, traits_type>::swap(rhs);
104 }
105
106 /**
107 * 27.9.1.4, members:
108 */
109
110 bool is_open() const
111 {
112 return file_ != nullptr;
113 }
114
115 basic_filebuf<char_type, traits_type>* open(const char* name, ios_base::openmode mode)
116 {
117 if (file_)
118 return nullptr;
119
120 mode_ = mode;
121 mode = (mode & (~ios_base::ate));
122 const char* mode_str = get_mode_str_(mode);
123
124 if (!mode_str)
125 return nullptr;
126
127 file_ = fopen(name, mode_str);
128 if (!file_)
129 return nullptr;
130
131 if ((mode_ & ios_base::ate) != 0)
132 {
133 if (fseek(file_, 0, SEEK_END) != 0)
134 {
135 close();
136 return nullptr;
137 }
138 }
139
140 if (!ibuf_ && mode_is_in_(mode_))
141 ibuf_ = new char_type[buf_size_];
142 if (!obuf_ && mode_is_out_(mode_))
143 obuf_ = new char_type[buf_size_];
144 init_();
145
146 return this;
147 }
148
149 basic_filebuf<char_type, traits_type>* open(const string& name, ios_base::openmode mode)
150 {
151 return open(name.c_str(), mode);
152 }
153
154 basic_filebuf<char_type, traits_type>* close()
155 {
156 // TODO: caught exceptions are to be rethrown after closing the file
157 if (!file_)
158 return nullptr;
159 // TODO: deallocate buffers?
160
161 if (this->output_next_ != this->output_begin_)
162 overflow(traits_type::eof());
163 // TODO: unshift? (p. 1084 at the top)
164
165 fclose(file_);
166 file_ = nullptr;
167
168 return this;
169 }
170
171 protected:
172 /**
173 * 27.9.1.5, overriden virtual functions:
174 */
175
176 int_type underflow() override
177 {
178 // TODO: use codecvt
179 if (!mode_is_in_(mode_))
180 return traits_type::eof();
181
182 if (!ibuf_)
183 {
184 ibuf_ = new char_type[buf_size_];
185 this->input_begin_ = this->input_next_ = this->input_end_ = ibuf_;
186 }
187
188 off_type i{};
189 if (this->input_next_ < this->input_end_)
190 {
191 auto idx = static_cast<off_type>(this->input_next_ - this->input_begin_);
192 auto count = static_cast<off_type>(buf_size_) - idx;
193
194 for (; i < count; ++i, ++idx)
195 ibuf_[i] = ibuf_[idx];
196 }
197
198 for (; i < static_cast<off_type>(buf_size_); ++i)
199 {
200 auto c = fgetc(file_);
201 if (c == traits_type::eof())
202 break;
203
204 ibuf_[i] = static_cast<char_type>(c);
205
206 if (ibuf_[i] == '\n')
207 {
208 ++i;
209 break;
210 }
211 }
212
213 this->input_next_ = this->input_begin_;
214 this->input_end_ = this->input_begin_ + i;
215
216 if (i == 0)
217 return traits_type::eof();
218
219 return traits_type::to_int_type(*this->input_next_);
220 }
221
222 int_type pbackfail(int_type c = traits_type::eof()) override
223 {
224 auto cc = traits_type::to_char_type(c);
225 if (!traits_type::eq_int_type(c, traits_type::eof()) &&
226 this->putback_avail_() &&
227 traits_type::eq(cc, this->gptr()[-1]))
228 {
229 --this->input_next_;
230
231 return c;
232 }
233 else if (!traits_type::eq_int_type(c, traits_type::eof()) &&
234 this->putback_avail_() && (mode_ & ios_base::out) != 0)
235 {
236 *--this->input_next_ = cc;
237
238 return c;
239 }
240 else if (traits_type::eq_int_type(c, traits_type::eof()) &&
241 this->putback_avail_())
242 {
243 --this->input_next_;
244
245 return traits_type::not_eof(c);
246 }
247
248 return traits_type::eof();
249 }
250
251 int_type overflow(int_type c = traits_type::eof()) override
252 {
253 // TODO: use codecvt
254 if (!mode_is_out_(mode_))
255 return traits_type::eof();
256
257 auto count = static_cast<size_t>(this->output_next_ - this->output_begin_);
258 fwrite(obuf_, sizeof(char_type), count, file_);
259 fflush(file_);
260
261 this->output_next_ = this->output_begin_;
262
263 return traits_type::not_eof(c);
264 }
265
266 basic_streambuf<char_type, traits_type>*
267 setbuf(char_type* s, streamsize n) override
268 {
269 // TODO: implement
270 __unimplemented();
271 return nullptr;
272 }
273
274 pos_type seekoff(off_type off, ios_base::seekdir dir,
275 ios_base::openmode mode = ios_base::in | ios_base::out) override
276 {
277 // TODO: implement
278 __unimplemented();
279 return pos_type{};
280 }
281
282 pos_type seekpos(pos_type pos,
283 ios_base::openmode mode = ios_base::in | ios_base::out) override
284 {
285 // TODO: implement
286 __unimplemented();
287 return pos_type{};
288 }
289
290 int sync() override
291 {
292 if (mode_is_out_(mode_))
293 overflow();
294
295 return 0; // TODO: what is the correct return value?
296 }
297
298 void imbue(const locale& loc) override
299 {
300 // TODO: codecvt should be cached when we start using it
301 basic_streambuf<char_type, traits_type>::imbue(loc);
302 }
303
304 private:
305 char_type* obuf_;
306 char_type* ibuf_;
307
308 ios_base::openmode mode_;
309
310 FILE* file_;
311
312 static constexpr size_t buf_size_{128};
313
314 const char* get_mode_str_(ios_base::openmode mode)
315 {
316 /**
317 * See table 132.
318 */
319 switch (mode)
320 {
321 case ios_base::out:
322 case ios_base::out | ios_base::trunc:
323 return "w";
324 case ios_base::out | ios_base::app:
325 case ios_base::app:
326 return "a";
327 case ios_base::in:
328 return "r";
329 case ios_base::in | ios_base::out:
330 return "r+";
331 case ios_base::in | ios_base::out | ios_base::trunc:
332 return "w+";
333 case ios_base::in | ios_base::out | ios_base::app:
334 case ios_base::in | ios_base::app:
335 return "a+";
336 case ios_base::binary | ios_base::out:
337 case ios_base::binary | ios_base::out | ios_base::trunc:
338 return "wb";
339 case ios_base::binary | ios_base::out | ios_base::app:
340 case ios_base::binary | ios_base::app:
341 return "ab";
342 case ios_base::binary | ios_base::in:
343 return "rb";
344 case ios_base::binary | ios_base::in | ios_base::out:
345 return "r+b";
346 case ios_base::binary | ios_base::in | ios_base::out | ios_base::trunc:
347 return "w+b";
348 case ios_base::binary | ios_base::in | ios_base::out | ios_base::app:
349 case ios_base::binary | ios_base::in | ios_base::app:
350 return "a+b";
351 default:
352 return nullptr;
353 }
354 }
355
356 bool mode_is_in_(ios_base::openmode mode)
357 {
358 return (mode & ios_base::in) != 0;
359 }
360
361 bool mode_is_out_(ios_base::openmode mode)
362 {
363 return (mode & (ios_base::out | ios_base::app | ios_base::trunc)) != 0;
364 }
365
366 void init_()
367 {
368 if (ibuf_)
369 this->input_begin_ = this->input_next_ = this->input_end_ = ibuf_;
370
371 if (obuf_)
372 {
373 this->output_begin_ = this->output_next_ = obuf_;
374 this->output_end_ = obuf_ + buf_size_;
375 }
376 }
377 };
378
379 template<class Char, class Traits>
380 void swap(basic_filebuf<Char, Traits>& lhs, basic_filebuf<Char, Traits>& rhs)
381 {
382 lhs.swap(rhs);
383 }
384
385 /**
386 * 27.9.1.6, class template basic_ifstream:
387 */
388
389 template<class Char, class Traits>
390 class basic_ifstream: public basic_istream<Char, Traits>
391 {
392 public:
393 using char_type = Char;
394 using traits_type = Traits;
395 using int_type = typename traits_type::int_type;
396 using pos_type = typename traits_type::pos_type;
397 using off_type = typename traits_type::off_type;
398
399 /**
400 * 27.9.1.7, constructors:
401 */
402
403 basic_ifstream()
404 : basic_istream<char_type, traits_type>{&rdbuf_},
405 rdbuf_{}
406 { /* DUMMY BODY */ }
407
408 explicit basic_ifstream(const char* name, ios_base::openmode mode = ios_base::in)
409 : basic_istream<char_type, traits_type>{&rdbuf_},
410 rdbuf_{}
411 {
412 if (!rdbuf_.open(name, mode | ios_base::in))
413 this->setstate(ios_base::failbit);
414 }
415
416 explicit basic_ifstream(const string& name, ios_base::openmode mode = ios_base::in)
417 : basic_istream<char_type, traits_type>{&rdbuf_},
418 rdbuf_{}
419 {
420 if (!rdbuf_.open(name, mode | ios_base::in))
421 this->setstate(ios_base::failbit);
422 }
423
424 basic_ifstream(const basic_ifstream&) = delete;
425
426 basic_ifstream(basic_ifstream&& other)
427 : basic_istream<char_type, traits_type>{move(other)},
428 rdbuf_{other.rdbuf_}
429 {
430 basic_istream<char_type, traits_type>::set_rdbuf(&rdbuf_);
431 }
432
433 /**
434 * 27.9.1.8, assign/swap:
435 */
436
437 basic_ifstream& operator=(const basic_ifstream&) = delete;
438
439 basic_ifstream& operator=(basic_ifstream&& other)
440 {
441 swap(other);
442 }
443
444 void swap(basic_ifstream& rhs)
445 {
446 basic_istream<char_type, traits_type>::swap(rhs);
447 rdbuf_.swap(rhs.rdbuf_);
448 }
449
450 /**
451 * 27.9.1.9, members:
452 */
453
454 basic_filebuf<char_type, traits_type>* rdbuf() const
455 {
456 return const_cast<basic_filebuf<char_type, traits_type>*>(&rdbuf_);
457 }
458
459 bool is_open() const
460 {
461 return rdbuf_.is_open();
462 }
463
464 void open(const char* name, ios_base::openmode mode = ios_base::in)
465 {
466 if (!rdbuf_.open(name, mode | ios_base::in))
467 this->setstate(ios_base::failbit);
468 else
469 this->clear();
470 }
471
472 void open(const string& name, ios_base::openmode mode = ios_base::in)
473 {
474 open(name.c_str(), mode);
475 }
476
477 void close()
478 {
479 if (!rdbuf_.close())
480 this->setstate(ios_base::failbit);
481 }
482
483 private:
484 basic_filebuf<char_type, traits_type> rdbuf_;
485 };
486
487 template<class Char, class Traits>
488 void swap(basic_ifstream<Char, Traits>& lhs,
489 basic_ifstream<Char, Traits>& rhs)
490 {
491 lhs.swap(rhs);
492 }
493
494 /**
495 * 27.9.1.10, class template basic_ofstream:
496 */
497
498 template<class Char, class Traits>
499 class basic_ofstream: public basic_ostream<Char, Traits>
500 {
501 public:
502 using char_type = Char;
503 using traits_type = Traits;
504 using int_type = typename traits_type::int_type;
505 using pos_type = typename traits_type::pos_type;
506 using off_type = typename traits_type::off_type;
507
508 /**
509 * 27.9.1.11, constructors:
510 */
511
512 basic_ofstream()
513 : basic_ostream<char_type, traits_type>{&rdbuf_},
514 rdbuf_{}
515 { /* DUMMY BODY */ }
516
517 explicit basic_ofstream(const char* name, ios_base::openmode mode = ios_base::out)
518 : basic_ostream<char_type, traits_type>{&rdbuf_},
519 rdbuf_{}
520 {
521 if (!rdbuf_.open(name, mode | ios_base::out))
522 this->setstate(ios_base::failbit);
523 }
524
525 explicit basic_ofstream(const string& name, ios_base::openmode mode = ios_base::out)
526 : basic_ostream<char_type, traits_type>{&rdbuf_},
527 rdbuf_{}
528 {
529 if (!rdbuf_.open(name, mode | ios_base::out))
530 this->setstate(ios_base::failbit);
531 }
532
533 basic_ofstream(const basic_ofstream&) = delete;
534
535 basic_ofstream(basic_ofstream&& other)
536 : basic_ostream<char_type, traits_type>{move(other)},
537 rdbuf_{other.rdbuf_}
538 {
539 basic_ostream<char_type, traits_type>::set_rdbuf(&rdbuf_);
540 }
541
542 /**
543 * 27.9.1.12, assign/swap:
544 */
545
546 basic_ofstream& operator=(const basic_ofstream&) = delete;
547
548 basic_ofstream& operator=(basic_ofstream&& other)
549 {
550 swap(other);
551 }
552
553 void swap(basic_ofstream& rhs)
554 {
555 basic_ostream<char_type, traits_type>::swap(rhs);
556 rdbuf_.swap(rhs.rdbuf_);
557 }
558
559 /**
560 * 27.9.1.13, members:
561 */
562
563 basic_filebuf<char_type, traits_type>* rdbuf() const
564 {
565 return const_cast<basic_filebuf<char_type, traits_type>*>(&rdbuf_);
566 }
567
568 bool is_open() const
569 {
570 return rdbuf_.is_open();
571 }
572
573 void open(const char* name, ios_base::openmode mode = ios_base::out)
574 {
575 if (!rdbuf_.open(name, mode | ios_base::out))
576 this->setstate(ios_base::failbit);
577 else
578 this->clear();
579 }
580
581 void open(const string& name, ios_base::openmode mode = ios_base::out)
582 {
583 open(name.c_str(), mode);
584 }
585
586 void close()
587 {
588 if (!rdbuf_.close())
589 this->setstate(ios_base::failbit);
590 }
591
592 private:
593 basic_filebuf<char_type, traits_type> rdbuf_;
594 };
595
596 template<class Char, class Traits>
597 void swap(basic_ofstream<Char, Traits>& lhs,
598 basic_ofstream<Char, Traits>& rhs)
599 {
600 lhs.swap(rhs);
601 }
602
603 /**
604 * 27.9.1.14, class template basic_fstream:
605 */
606
607 template<class Char, class Traits>
608 class basic_fstream: public basic_iostream<Char, Traits>
609 {
610 public:
611 using char_type = Char;
612 using traits_type = Traits;
613 using int_type = typename traits_type::int_type;
614 using pos_type = typename traits_type::pos_type;
615 using off_type = typename traits_type::off_type;
616
617 /**
618 * 27.9.1.15, constructors:
619 */
620
621 basic_fstream()
622 : basic_iostream<char_type, traits_type>{&rdbuf_},
623 rdbuf_{}
624 { /* DUMMY BODY */ }
625
626 explicit basic_fstream(const char* name,
627 ios_base::openmode mode = ios_base::in | ios_base::out)
628 : basic_iostream<char_type, traits_type>{&rdbuf_},
629 rdbuf_{}
630 {
631 if (!rdbuf_.open(name, mode))
632 this->setstate(ios_base::failbit);
633 }
634
635 explicit basic_fstream(const string& name,
636 ios_base::openmode mode = ios_base::in | ios_base::out)
637 : basic_iostream<char_type, traits_type>{&rdbuf_},
638 rdbuf_{}
639 {
640 if (!rdbuf_.open(name, mode))
641 this->setstate(ios_base::failbit);
642 }
643
644 basic_fstream(const basic_fstream&) = delete;
645
646 basic_fstream(basic_fstream&& other)
647 : basic_iostream<char_type, traits_type>{move(other)},
648 rdbuf_{other.rdbuf_}
649 {
650 basic_iostream<char_type, traits_type>::set_rdbuf(&rdbuf_);
651 }
652
653 /**
654 * 27.9.1.16, assign/swap:
655 */
656
657 basic_fstream& operator=(const basic_fstream&) = delete;
658
659 basic_fstream& operator=(basic_fstream&& other)
660 {
661 swap(other);
662 }
663
664 void swap(basic_fstream& rhs)
665 {
666 basic_iostream<char_type, traits_type>::swap(rhs);
667 rdbuf_.swap(rhs.rdbuf_);
668 }
669
670 /**
671 * 27.9.1.17, members:
672 */
673
674 basic_filebuf<char_type, traits_type>* rdbuf() const
675 {
676 return const_cast<basic_filebuf<char_type, traits_type>*>(&rdbuf_);
677 }
678
679 bool is_open() const
680 {
681 return rdbuf_.is_open();
682 }
683
684 void open(const char* name,
685 ios_base::openmode mode = ios_base::in | ios_base::out)
686 {
687 if (!rdbuf_.open(name, mode))
688 this->setstate(ios_base::failbit);
689 else
690 this->clear();
691 }
692
693 void open(const string& name,
694 ios_base::openmode mode = ios_base::in | ios_base::out)
695 {
696 open(name.c_str(), mode);
697 }
698
699 void close()
700 {
701 if (!rdbuf_.close())
702 this->setstate(ios_base::failbit);
703 }
704
705 private:
706 basic_filebuf<char_type, traits_type> rdbuf_;
707 };
708
709 template<class Char, class Traits>
710 void swap(basic_fstream<Char, Traits>& lhs,
711 basic_fstream<Char, Traits>& rhs)
712 {
713 lhs.swap(rhs);
714 }
715}
716
717#endif
Note: See TracBrowser for help on using the repository browser.