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
30namespace android {
31namespace 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//
46template <class _CharT>
47class 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
83template <class _CharT>
84stdio_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
91template <class _CharT>
92stdio_filebuf<_CharT>::~stdio_filebuf() {
93  if (__file_)
94    fclose(__file_);
95}
96
97template <class _CharT>
98void 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
106template <class _CharT>
107typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::underflow() {
108  return __getchar(false);
109}
110
111template <class _CharT>
112typename stdio_filebuf<_CharT>::int_type stdio_filebuf<_CharT>::uflow() {
113  return __getchar(true);
114}
115
116template <class _CharT>
117typename 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
179template <class _CharT>
180typename 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