Skip to content

Commit 9f20452

Browse files
committed
operator>> for value
1 parent 77c05d2 commit 9f20452

File tree

4 files changed

+164
-1
lines changed

4 files changed

+164
-1
lines changed

include/boost/json/impl/value.ipp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@
1111
#define BOOST_JSON_IMPL_VALUE_IPP
1212

1313
#include <boost/json/value.hpp>
14+
#include <boost/json/parser.hpp>
1415
#include <boost/json/detail/hash_combine.hpp>
1516
#include <cstring>
17+
#include <istream>
1618
#include <limits>
1719
#include <new>
1820
#include <utility>
@@ -343,6 +345,63 @@ swap(value& other)
343345
::new(this) value(pilfer(temp2));
344346
}
345347

348+
std::istream&
349+
operator>>(
350+
std::istream& is,
351+
value& jv)
352+
{
353+
using Traits = std::istream::traits_type;
354+
355+
std::istream::sentry sentry(is);
356+
if( !sentry )
357+
return is;
358+
359+
unsigned char parser_buf[BOOST_JSON_STACK_BUFFER_SIZE];
360+
stream_parser p({}, {}, parser_buf);
361+
p.reset( jv.storage() );
362+
363+
std::ios::iostate err = std::ios::goodbit;
364+
try
365+
{
366+
std::istream::int_type c = is.rdbuf()->sgetc();
367+
while( true )
368+
{
369+
error_code ec;
370+
371+
if( Traits::eq_int_type(c, Traits::eof()) )
372+
{
373+
err |= std::ios::eofbit;
374+
p.finish(ec);
375+
if( ec.failed() )
376+
break;
377+
}
378+
379+
if( p.done() )
380+
{
381+
jv = p.release();
382+
return is;
383+
}
384+
385+
char read_buf[1];
386+
read_buf[0] = Traits::to_char_type(c);
387+
c = is.rdbuf()->snextc();
388+
389+
p.write_some(read_buf, 1, ec);
390+
if( ec.failed() )
391+
break;
392+
}
393+
}
394+
catch(...)
395+
{
396+
is.setstate(std::ios::failbit);
397+
throw;
398+
}
399+
400+
err |= std::ios::failbit;
401+
is.setstate(err);
402+
return is;
403+
}
404+
346405
//----------------------------------------------------------
347406
//
348407
// private

include/boost/json/value.hpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3339,6 +3339,34 @@ class value
33393339
std::ostream& os,
33403340
value const& jv);
33413341

3342+
/** Parse @ref value from an input stream.
3343+
3344+
This function parses JSON from an input stream into a `value`. If
3345+
parsing fails, `std::ios_base::failbit` will be set for `is` and
3346+
`jv` will be left unchanged.
3347+
3348+
@return Reference to `is`.
3349+
3350+
@par Complexity
3351+
Linear in the size of JSON data.
3352+
3353+
@par Exception Safety
3354+
Basic guarantee.
3355+
Calls to `memory_resource::allocate` may throw.
3356+
The stream may throw as described by
3357+
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
3358+
3359+
@param is The input stream to parse from.
3360+
3361+
@param jv The value to parse into.
3362+
*/
3363+
BOOST_JSON_DECL
3364+
friend
3365+
std::istream&
3366+
operator>>(
3367+
std::istream& is,
3368+
value& jv);
3369+
33423370
private:
33433371
static
33443372
void

test/test_suite.hpp

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -626,9 +626,13 @@ current_function_helper()
626626
*/
627627
using log_type = detail::log_ostream<char>;
628628

629+
#define BOOST_JSON_PP_DO_CONCAT(x, y) x ## y
630+
631+
#define BOOST_JSON_PP_CONCAT(x, y) BOOST_JSON_PP_DO_CONCAT(x, y)
632+
629633
#define BOOST_TEST_CHECKPOINT(...) \
630634
::test_suite::detail::checkpoint \
631-
_BOOST_TEST_CHECKPOINT ## __LINE__ ( \
635+
BOOST_JSON_PP_CONCAT(_BOOST_TEST_CHECKPOINT, __LINE__) ( \
632636
__FILE__, __LINE__, __VA_ARGS__ + 0)
633637

634638
#define BOOST_TEST(expr) \

test/value.cpp

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,27 @@ T min_of()
4040
return (std::numeric_limits<T>::min)();
4141
}
4242

43+
class throwing_buffer
44+
: public std::streambuf
45+
{
46+
protected:
47+
int_type
48+
underflow() override
49+
{
50+
// make sure we don't throw before the parser had the chance to start
51+
if( !gptr() )
52+
{
53+
setg(buf_, buf_, buf_ + 1);
54+
return *buf_;
55+
}
56+
57+
throw std::invalid_argument("this buffer throws");
58+
}
59+
60+
private:
61+
char buf_[1] = {'t'};
62+
};
63+
4364
} // (anon)
4465

4566
class value_test
@@ -2233,6 +2254,56 @@ class value_test
22332254
value({{"a", "b"}})));
22342255
}
22352256

2257+
void
2258+
testIstream()
2259+
{
2260+
std::istringstream ss(
2261+
R"({ "x": 1
2262+
, "y": 2
2263+
, "z": [77, null, true, "qwerty uiop"]
2264+
} 12)");
2265+
value jv;
2266+
2267+
ss >> jv;
2268+
BOOST_TEST( !!ss );
2269+
BOOST_TEST((
2270+
jv == value{
2271+
{"x", 1},
2272+
{"y", 2},
2273+
{"z", {77, nullptr, true, "qwerty uiop"}}} ));
2274+
2275+
// check we didn't consume any extra characters
2276+
std::string s;
2277+
std::getline(ss, s);
2278+
BOOST_TEST( s == " 12" );
2279+
2280+
ss.clear();
2281+
ss.str("23");
2282+
ss >> jv;
2283+
BOOST_TEST( jv == 23 );
2284+
2285+
ss.clear();
2286+
ss.str("");
2287+
ss >> jv;
2288+
BOOST_TEST( ss.rdstate() == (std::ios::failbit | std::ios::eofbit) );
2289+
2290+
ss.clear();
2291+
ss.str("nu");
2292+
ss >> jv;
2293+
BOOST_TEST( ss.rdstate() == (std::ios::failbit | std::ios::eofbit) );
2294+
2295+
ss.clear();
2296+
ss.str("[1,2,3,4,]");
2297+
ss >> jv;
2298+
BOOST_TEST( ss.rdstate() == std::ios::failbit );
2299+
2300+
{
2301+
throwing_buffer buf;
2302+
std::istream is(&buf);
2303+
BOOST_TEST_THROWS( is >> jv, std::exception );
2304+
}
2305+
}
2306+
22362307
//------------------------------------------------------
22372308

22382309
void
@@ -2255,6 +2326,7 @@ class value_test
22552326
testInitList();
22562327
testEquality();
22572328
testHash();
2329+
testIstream();
22582330
}
22592331
};
22602332

0 commit comments

Comments
 (0)