@@ -352,22 +352,30 @@ operator>>(
352352{
353353 using Traits = std::istream::traits_type;
354354
355+ // sentry prepares the stream for reading and finalizes it in destructor
355356 std::istream::sentry sentry (is);
356357 if ( !sentry )
357358 return is;
358359
359- unsigned char parser_buf[BOOST_JSON_STACK_BUFFER_SIZE];
360+ unsigned char parser_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2 ];
360361 stream_parser p ({}, {}, parser_buf);
361362 p.reset ( jv.storage () );
362363
364+ char read_buf[BOOST_JSON_STACK_BUFFER_SIZE / 2 ];
365+ std::streambuf& buf = *is.rdbuf ();
363366 std::ios::iostate err = std::ios::goodbit;
364367 try
365368 {
366- std::istream::int_type c = is.rdbuf ()->sgetc ();
367369 while ( true )
368370 {
369371 error_code ec;
370372
373+ // we peek the buffer; this either makes sure that there's no
374+ // more input, or makes sure there's something in the internal
375+ // buffer (so in_avail will return a positive number)
376+ std::istream::int_type c = is.rdbuf ()->sgetc ();
377+ // if we indeed reached EOF, we check if we parsed a full JSON
378+ // document; if not, we error out
371379 if ( Traits::eq_int_type (c, Traits::eof ()) )
372380 {
373381 err |= std::ios::eofbit;
@@ -376,29 +384,56 @@ operator>>(
376384 break ;
377385 }
378386
387+ // regardless of reaching EOF, we might have parsed a full JSON
388+ // document; if so, we successfully finish
379389 if ( p.done () )
380390 {
381391 jv = p.release ();
382392 return is;
383393 }
384394
385- char read_buf[1 ];
386- read_buf[0 ] = Traits::to_char_type (c);
387- c = is.rdbuf ()->snextc ();
395+ // at this point we definitely have more input, specifically in
396+ // buf's internal buffer; we also definitely haven't parsed a whole
397+ // document
398+ std::streamsize available = buf.in_avail ();
399+ // if this assert fails, the streambuf is buggy
400+ BOOST_ASSERT ( available > 0 );
401+
402+ available = std::min (
403+ static_cast <std::size_t >(available), sizeof (read_buf) );
404+ // we read from the internal buffer of buf into our buffer
405+ available = buf.sgetn ( read_buf, available );
406+
407+ std::size_t consumed = p.write_some ( read_buf, available, ec );
408+ // if the parser hasn't consumed the entire input we've took from
409+ // buf, we put the remaining data back; this should succeed,
410+ // because we only read data from buf's internal buffer
411+ while ( consumed++ < static_cast <std::size_t >(available) )
412+ {
413+ std::istream::int_type const status = buf.sungetc ();
414+ BOOST_ASSERT ( status != Traits::eof () );
415+ (void )status;
416+ }
388417
389- p.write_some (read_buf, 1 , ec);
390418 if ( ec.failed () )
391419 break ;
392420 }
393421 }
394422 catch (...)
395423 {
396- is.setstate (std::ios::failbit);
397- throw ;
424+ try
425+ {
426+ is.setstate (std::ios::badbit);
427+ }
428+ // we ignore the exception, because we need to throw the original
429+ // exception instead
430+ catch ( std::ios::failure const & ) { }
431+
432+ if ( is.exceptions () & std::ios::badbit )
433+ throw ;
398434 }
399435
400- err |= std::ios::failbit;
401- is.setstate (err);
436+ is.setstate (err | std::ios::failbit);
402437 return is;
403438}
404439
0 commit comments