1 // Copyright (c) 2009-2014 by the contributors listed in CREDITS.TXT
2 // Copyright (c) 2016 Google, Inc.
3 //
4 // Permission is hereby granted, free of charge, to any person obtaining a copy
5 // of this software and associated documentation files (the "Software"), to deal
6 // in the Software without restriction, including without limitation the rights
7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 // copies of the Software, and to permit persons to whom the Software is
9 // furnished to do so, subject to the following conditions:
10 //
11 // The above copyright notice and this permission notice shall be included in
12 // all copies or substantial portions of the Software.
13 //
14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 // THE SOFTWARE.
21 
22 #ifndef ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
23 #define ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
24 
25 #include <cstdio>
26 #include <istream>
27 #include <locale>
28 #include <streambuf>
29 
30 namespace android {
31 namespace dvr {
32 
33 // An implementation of std::basic_streambuf backed by a FILE pointer. This is
34 // ported from the internal llvm-libc++ support for std::cin. It's really
35 // unfortunate that we have to do this, but the C++11 standard is too pendantic
36 // to support creating streams from file descriptors or FILE pointers. This
37 // implementation uses all standard interfaces, except for the call to
38 // std::__throw_runtime_error(), which is only needed to deal with exceeding
39 // locale encoding limits. This class is meant to be used for reading system
40 // files, which don't require exotic locale support, so this call could be
41 // removed in the future, if necessary.
42 //
43 // Original source file: llvm-libcxx/llvm-libc++/include/__std_stream
44 // Original class name: __stdinbuf
45 //
46 template <class _CharT>
47 class stdio_filebuf
48     : public std::basic_streambuf<_CharT, std::char_traits<_CharT> > {
49  public:
50   typedef _CharT char_type;
51   typedef std::char_traits<char_type> traits_type;
52   typedef typename traits_type::int_type int_type;
53   typedef typename traits_type::pos_type pos_type;
54   typedef typename traits_type::off_type off_type;
55   typedef typename traits_type::state_type state_type;
56 
57   explicit stdio_filebuf(FILE* __fp);
58   ~stdio_filebuf() override;
59 
60  protected:
61   virtual int_type underflow() override;
62   virtual int_type uflow() override;
63   virtual int_type pbackfail(int_type __c = traits_type::eof()) override;
64   virtual void imbue(const std::locale& __loc) override;
65 
66  private:
67   FILE* __file_;
68   const std::codecvt<char_type, char, state_type>* __cv_;
69   state_type __st_;
70   int __encoding_;
71   int_type __last_consumed_;
72   bool __last_consumed_is_next_;
73   bool __always_noconv_;
74 
75   stdio_filebuf(const stdio_filebuf&);
76   stdio_filebuf& operator=(const stdio_filebuf&);
77 
78   int_type __getchar(bool __consume);
79 
80   static const int __limit = 8;
81 };
82 
83 template <class _CharT>
stdio_filebuf(FILE * __fp)84 stdio_filebuf<_CharT>::stdio_filebuf(FILE* __fp)
85     : __file_(__fp),
86       __last_consumed_(traits_type::eof()),
87       __last_consumed_is_next_(false) {
88   imbue(this->getloc());
89 }
90 
91 template <class _CharT>
~stdio_filebuf()92 stdio_filebuf<_CharT>::~stdio_filebuf() {
93   if (__file_)
94     fclose(__file_);
95 }
96 
97 template <class _CharT>
imbue(const std::locale & __loc)98 void stdio_filebuf<_CharT>::imbue(const std::locale& __loc) {
99   __cv_ = &std::use_facet<std::codecvt<char_type, char, state_type> >(__loc);
100   __encoding_ = __cv_->encoding();
101   __always_noconv_ = __cv_->always_noconv();
102   if (__encoding_ > __limit)
103     std::__throw_runtime_error("unsupported locale for standard io");
104 }
105 
106 template <class _CharT>
underflow()107 typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::underflow() {
108   return __getchar(false);
109 }
110 
111 template <class _CharT>
uflow()112 typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::uflow() {
113   return __getchar(true);
114 }
115 
116 template <class _CharT>
__getchar(bool __consume)117 typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::__getchar(
118     bool __consume) {
119   if (__last_consumed_is_next_) {
120     int_type __result = __last_consumed_;
121     if (__consume) {
122       __last_consumed_ = traits_type::eof();
123       __last_consumed_is_next_ = false;
124     }
125     return __result;
126   }
127   char __extbuf[__limit];
128   int __nread = std::max(1, __encoding_);
129   for (int __i = 0; __i < __nread; ++__i) {
130     int __c = getc(__file_);
131     if (__c == EOF)
132       return traits_type::eof();
133     __extbuf[__i] = static_cast<char>(__c);
134   }
135   char_type __1buf;
136   if (__always_noconv_)
137     __1buf = static_cast<char_type>(__extbuf[0]);
138   else {
139     const char* __enxt;
140     char_type* __inxt;
141     std::codecvt_base::result __r;
142     do {
143       state_type __sv_st = __st_;
144       __r = __cv_->in(__st_, __extbuf, __extbuf + __nread, __enxt, &__1buf,
145                       &__1buf + 1, __inxt);
146       switch (__r) {
147         case std::codecvt_base::ok:
148           break;
149         case std::codecvt_base::partial:
150           __st_ = __sv_st;
151           if (__nread == sizeof(__extbuf))
152             return traits_type::eof();
153           {
154             int __c = getc(__file_);
155             if (__c == EOF)
156               return traits_type::eof();
157             __extbuf[__nread] = static_cast<char>(__c);
158           }
159           ++__nread;
160           break;
161         case std::codecvt_base::error:
162           return traits_type::eof();
163         case std::codecvt_base::noconv:
164           __1buf = static_cast<char_type>(__extbuf[0]);
165           break;
166       }
167     } while (__r == std::codecvt_base::partial);
168   }
169   if (!__consume) {
170     for (int __i = __nread; __i > 0;) {
171       if (ungetc(traits_type::to_int_type(__extbuf[--__i]), __file_) == EOF)
172         return traits_type::eof();
173     }
174   } else
175     __last_consumed_ = traits_type::to_int_type(__1buf);
176   return traits_type::to_int_type(__1buf);
177 }
178 
179 template <class _CharT>
pbackfail(int_type __c)180 typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::pbackfail(
181     int_type __c) {
182   if (traits_type::eq_int_type(__c, traits_type::eof())) {
183     if (!__last_consumed_is_next_) {
184       __c = __last_consumed_;
185       __last_consumed_is_next_ =
186           !traits_type::eq_int_type(__last_consumed_, traits_type::eof());
187     }
188     return __c;
189   }
190   if (__last_consumed_is_next_) {
191     char __extbuf[__limit];
192     char* __enxt;
193     const char_type __ci = traits_type::to_char_type(__last_consumed_);
194     const char_type* __inxt;
195     switch (__cv_->out(__st_, &__ci, &__ci + 1, __inxt, __extbuf,
196                        __extbuf + sizeof(__extbuf), __enxt)) {
197       case std::codecvt_base::ok:
198         break;
199       case std::codecvt_base::noconv:
200         __extbuf[0] = static_cast<char>(__last_consumed_);
201         __enxt = __extbuf + 1;
202         break;
203       case std::codecvt_base::partial:
204       case std::codecvt_base::error:
205         return traits_type::eof();
206     }
207     while (__enxt > __extbuf)
208       if (ungetc(*--__enxt, __file_) == EOF)
209         return traits_type::eof();
210   }
211   __last_consumed_ = __c;
212   __last_consumed_is_next_ = true;
213   return __c;
214 }
215 
216 }  // namespace dvr
217 }  // namespace android
218 
219 #endif  // ANDROID_DVR_PERFORMANCED_STDIO_FILEBUF_H_
220