message: Restore istream performance
  o Use std::streambuf I/O instead of std::istream; does not maintain
  (unused) istream flags.
  o Further simplify get/unget handling.
  o Restore original handling of NUL in input stream; ignored during
  token_string escaping.
diff: @@ -1410,6 +1410,11 @@ using input_adapter_t = std::shared_ptr<input_adapter_protocol>;
  class input_stream_adapter : public input_adapter_protocol
  {
  public:
  + ~input_stream_adapter() override
  + {
  + // clear stream flags; we use underlying streambuf I/O, do not maintain ifstream flags
  + is.clear();
  + }
  explicit input_stream_adapter(std::istream& i)
  : is(i)
  {
  @@ -1441,17 +1446,15 @@ class input_stream_adapter : public input_adapter_protocol
  }
  }
  - ~input_stream_adapter() override {}
  -
  int get_character() override
  {
  - int c = is.get();
  + int c = is.rdbuf()->sbumpc(); // Avoided for performance: int c = is.get();
  return c == std::char_traits<char>::eof() ? c : ( c & 0xFF );
  }
  void unget_character() override
  {
  - is.unget();
  + is.rdbuf()->sungetc(); // Avoided for performance: is.unget();
  }
  private:
  @@ -1489,7 +1492,7 @@ class input_buffer_adapter : public input_adapter_protocol
  void unget_character() noexcept override
  {
  - if (JSON_LIKELY(cursor > 0))
  + if (JSON_LIKELY(cursor > start))
  {
  --cursor;
  }
  @@ -2652,17 +2655,16 @@ scan_number_done:
  void reset() noexcept
  {
  yylen = 0;
  - start_pos = chars_read - 1;
  - token_string = static_cast<char>( current );
  + token_string.clear();
  + token_string.push_back(static_cast<char>(current));
  }
  /// get a character from the input
  int get()
  {
  ++chars_read;
  -
  int c = current = ia->get_character();
  - token_string += static_cast<char>( c );
  + token_string.push_back(static_cast<char>(c));
  return c;
  }
  @@ -2670,9 +2672,12 @@ scan_number_done:
  void unget()
  {
  --chars_read;
  -
  - if (token_string.size() > 0)
  - token_string.resize( token_string.size() - 1 );
  + if (JSON_LIKELY(current != std::char_traits<char>::eof()))
  + {
  + ia->unget_character();
  + }
  + if (! token_string.empty())
  + token_string.pop_back();
  }
  /// add a character to yytext
  @@ -2736,7 +2741,9 @@ scan_number_done:
  std::string result;
  for (auto c : token_string)
  {
  - if ( c == std::char_traits<char>::eof() ) {
  + if (c == '\0' or c == std::char_traits<char>::eof())
  + {
  + // ignore EOF
  continue;
  }
  else if ('\x00' <= c and c <= '\x1f')
  @@ -2840,10 +2847,8 @@ scan_number_done:
  /// the number of characters read
  std::size_t chars_read = 0;
  - /// the start position of the current token
  - std::size_t start_pos = 0;
  /// raw input token string (for error messages)
  - std::string token_string = "";
  + std::vector<char> token_string = std::vector<char>();
  /// buffer for variable-length tokens (numbers, strings)
  std::vector<char> yytext = std::vector<char>(1024, '\0');
  