source: mainline/uspace/lib/cpp/include/impl/fstream.hpp@ d275344

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d275344 was 912f856, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: added fstream implementation

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