Horizon
input_adapters.hpp
1 #pragma once
2 
3 #include <algorithm> // min
4 #include <array> // array
5 #include <cassert> // assert
6 #include <cstddef> // size_t
7 #include <cstring> // strlen
8 #include <ios> // streamsize, streamoff, streampos
9 #include <istream> // istream
10 #include <iterator> // begin, end, iterator_traits, random_access_iterator_tag, distance, next
11 #include <memory> // shared_ptr, make_shared, addressof
12 #include <numeric> // accumulate
13 #include <string> // string, char_traits
14 #include <type_traits> // enable_if, is_base_of, is_pointer, is_integral, remove_pointer
15 #include <utility> // pair, declval
16 
17 #include <nlohmann/detail/macro_scope.hpp>
18 
19 namespace nlohmann
20 {
21 namespace detail
22 {
24 // input adapters //
26 
39 {
41  virtual std::char_traits<char>::int_type get_character() = 0;
43  virtual void unget_character() = 0;
44  virtual ~input_adapter_protocol() = default;
45 };
46 
48 using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
49 
60 {
61  public:
62  ~input_stream_adapter() override
63  {
64  // clear stream flags; we use underlying streambuf I/O, do not
65  // maintain ifstream flags
66  is.clear();
67  }
68 
69  explicit input_stream_adapter(std::istream& i)
70  : is(i), sb(*i.rdbuf())
71  {
72  // skip byte order mark
73  std::char_traits<char>::int_type c;
74  if ((c = get_character()) == 0xEF)
75  {
76  if ((c = get_character()) == 0xBB)
77  {
78  if ((c = get_character()) == 0xBF)
79  {
80  return; // Ignore BOM
81  }
82  else if (c != std::char_traits<char>::eof())
83  {
84  is.unget();
85  }
86  is.putback('\xBB');
87  }
88  else if (c != std::char_traits<char>::eof())
89  {
90  is.unget();
91  }
92  is.putback('\xEF');
93  }
94  else if (c != std::char_traits<char>::eof())
95  {
96  is.unget(); // no byte order mark; process as usual
97  }
98  }
99 
100  // delete because of pointer members
102  input_stream_adapter& operator=(input_stream_adapter&) = delete;
103 
104  // std::istream/std::streambuf use std::char_traits<char>::to_int_type, to
105  // ensure that std::char_traits<char>::eof() and the character 0xFF do not
106  // end up as the same value, eg. 0xFFFFFFFF.
107  std::char_traits<char>::int_type get_character() override
108  {
109  return sb.sbumpc();
110  }
111 
112  void unget_character() override
113  {
114  sb.sungetc(); // is.unget() avoided for performance
115  }
116 
117  private:
119  std::istream& is;
120  std::streambuf& sb;
121 };
122 
125 {
126  public:
127  input_buffer_adapter(const char* b, const std::size_t l)
128  : cursor(b), limit(b + l), start(b)
129  {
130  // skip byte order mark
131  if (l >= 3 and b[0] == '\xEF' and b[1] == '\xBB' and b[2] == '\xBF')
132  {
133  cursor += 3;
134  }
135  }
136 
137  // delete because of pointer members
139  input_buffer_adapter& operator=(input_buffer_adapter&) = delete;
140 
141  std::char_traits<char>::int_type get_character() noexcept override
142  {
143  if (JSON_LIKELY(cursor < limit))
144  {
145  return std::char_traits<char>::to_int_type(*(cursor++));
146  }
147 
148  return std::char_traits<char>::eof();
149  }
150 
151  void unget_character() noexcept override
152  {
153  if (JSON_LIKELY(cursor > start))
154  {
155  --cursor;
156  }
157  }
158 
159  private:
161  const char* cursor;
163  const char* limit;
165  const char* start;
166 };
167 
169 {
170  public:
171  // native support
172 
174  input_adapter(std::istream& i)
175  : ia(std::make_shared<input_stream_adapter>(i)) {}
176 
178  input_adapter(std::istream&& i)
179  : ia(std::make_shared<input_stream_adapter>(i)) {}
180 
182  template<typename CharT,
183  typename std::enable_if<
184  std::is_pointer<CharT>::value and
185  std::is_integral<typename std::remove_pointer<CharT>::type>::value and
186  sizeof(typename std::remove_pointer<CharT>::type) == 1,
187  int>::type = 0>
188  input_adapter(CharT b, std::size_t l)
189  : ia(std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(b), l)) {}
190 
191  // derived support
192 
194  template<typename CharT,
195  typename std::enable_if<
196  std::is_pointer<CharT>::value and
197  std::is_integral<typename std::remove_pointer<CharT>::type>::value and
198  sizeof(typename std::remove_pointer<CharT>::type) == 1,
199  int>::type = 0>
200  input_adapter(CharT b)
201  : input_adapter(reinterpret_cast<const char*>(b),
202  std::strlen(reinterpret_cast<const char*>(b))) {}
203 
205  template<class IteratorType,
206  typename std::enable_if<
207  std::is_same<typename std::iterator_traits<IteratorType>::iterator_category, std::random_access_iterator_tag>::value,
208  int>::type = 0>
209  input_adapter(IteratorType first, IteratorType last)
210  {
211  // assertion to check that the iterator range is indeed contiguous,
212  // see http://stackoverflow.com/a/35008842/266378 for more discussion
213  assert(std::accumulate(
214  first, last, std::pair<bool, int>(true, 0),
215  [&first](std::pair<bool, int> res, decltype(*first) val)
216  {
217  res.first &= (val == *(std::next(std::addressof(*first), res.second++)));
218  return res;
219  }).first);
220 
221  // assertion to check that each element is 1 byte long
222  static_assert(
223  sizeof(typename std::iterator_traits<IteratorType>::value_type) == 1,
224  "each element in the iterator range must have the size of 1 byte");
225 
226  const auto len = static_cast<size_t>(std::distance(first, last));
227  if (JSON_LIKELY(len > 0))
228  {
229  // there is at least one element: use the address of first
230  ia = std::make_shared<input_buffer_adapter>(reinterpret_cast<const char*>(&(*first)), len);
231  }
232  else
233  {
234  // the address of first cannot be used: use nullptr
235  ia = std::make_shared<input_buffer_adapter>(nullptr, len);
236  }
237  }
238 
240  template<class T, std::size_t N>
242  : input_adapter(std::begin(array), std::end(array)) {}
243 
245  template<class ContiguousContainer, typename
246  std::enable_if<not std::is_pointer<ContiguousContainer>::value and
247  std::is_base_of<std::random_access_iterator_tag, typename std::iterator_traits<decltype(std::begin(std::declval<ContiguousContainer const>()))>::iterator_category>::value,
248  int>::type = 0>
249  input_adapter(const ContiguousContainer& c)
250  : input_adapter(std::begin(c), std::end(c)) {}
251 
252  operator input_adapter_t()
253  {
254  return ia;
255  }
256 
257  private:
259  input_adapter_t ia = nullptr;
260 };
261 }
262 }
nlohmann::detail::input_buffer_adapter::get_character
std::char_traits< char >::int_type get_character() noexcept override
get a character [0,255] or std::char_traits<char>::eof().
Definition: input_adapters.hpp:141
nlohmann::detail::input_stream_adapter::get_character
std::char_traits< char >::int_type get_character() override
get a character [0,255] or std::char_traits<char>::eof().
Definition: input_adapters.hpp:107
nlohmann::detail::input_stream_adapter
Definition: input_adapters.hpp:59
nlohmann
namespace for Niels Lohmann
Definition: adl_serializer.hpp:8
nlohmann::detail::input_buffer_adapter
input adapter for buffer input
Definition: input_adapters.hpp:124
nlohmann::detail::input_adapter::input_adapter
input_adapter(T(&array)[N])
input adapter for array
Definition: input_adapters.hpp:241
nlohmann::detail::input_adapter_t
std::shared_ptr< input_adapter_protocol > input_adapter_t
a type to simplify interfaces
Definition: input_adapters.hpp:48
nlohmann::detail::input_buffer_adapter::unget_character
void unget_character() noexcept override
restore the last non-eof() character to input
Definition: input_adapters.hpp:151
nlohmann::detail::input_adapter_protocol
abstract input adapter interface
Definition: input_adapters.hpp:38
nlohmann::detail::input_adapter_protocol::unget_character
virtual void unget_character()=0
restore the last non-eof() character to input
nlohmann::detail::value_t::array
array (ordered collection of values)
nlohmann::detail::input_adapter::input_adapter
input_adapter(const ContiguousContainer &c)
input adapter for contiguous container
Definition: input_adapters.hpp:249
nlohmann::detail::input_adapter::input_adapter
input_adapter(IteratorType first, IteratorType last)
input adapter for iterator range with contiguous storage
Definition: input_adapters.hpp:209
nlohmann::detail::input_adapter::input_adapter
input_adapter(CharT b, std::size_t l)
input adapter for buffer
Definition: input_adapters.hpp:188
nlohmann::detail::input_adapter
Definition: input_adapters.hpp:168
nlohmann::detail::input_adapter::input_adapter
input_adapter(std::istream &i)
input adapter for input stream
Definition: input_adapters.hpp:174
nlohmann::detail::input_adapter_protocol::get_character
virtual std::char_traits< char >::int_type get_character()=0
get a character [0,255] or std::char_traits<char>::eof().
nlohmann::detail::input_stream_adapter::unget_character
void unget_character() override
restore the last non-eof() character to input
Definition: input_adapters.hpp:112
nlohmann::detail::input_adapter::input_adapter
input_adapter(CharT b)
input adapter for string literal
Definition: input_adapters.hpp:200
nlohmann::detail::input_adapter::input_adapter
input_adapter(std::istream &&i)
input adapter for input stream
Definition: input_adapters.hpp:178