Skip to content

Commit 3404427

Browse files
committed
parse overload for std::istream
1 parent 9f20452 commit 3404427

File tree

7 files changed

+198
-2
lines changed

7 files changed

+198
-2
lines changed

include/boost/json/error.hpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,9 @@ enum class error
6363
/// A string is too large
6464
string_too_large,
6565

66+
/// error occured when trying to read input
67+
input_error,
68+
6669
//
6770
// generic errors
6871
//

include/boost/json/impl/error.ipp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ case error::object_too_large: return "object too large";
4444
case error::array_too_large: return "array too large";
4545
case error::key_too_large: return "key too large";
4646
case error::string_too_large: return "string too large";
47+
case error::input_error: return "input error";
4748

4849
case error::exception: return "got exception";
4950
case error::test_failure: return "test failure";
@@ -91,6 +92,7 @@ case error::object_too_large:
9192
case error::array_too_large:
9293
case error::key_too_large:
9394
case error::string_too_large:
95+
case error::input_error:
9496
return condition::parse_error;
9597

9698
case error::missing_slash:

include/boost/json/impl/parse.ipp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include <boost/json/parser.hpp>
1616
#include <boost/json/detail/except.hpp>
1717

18+
#include <istream>
19+
1820
BOOST_JSON_NS_BEGIN
1921

2022
value
@@ -62,6 +64,73 @@ parse(
6264
return jv;
6365
}
6466

67+
value
68+
parse(
69+
std::istream& is,
70+
error_code& ec,
71+
storage_ptr sp,
72+
parse_options const& opt)
73+
{
74+
unsigned char parser_buffer[BOOST_JSON_STACK_BUFFER_SIZE / 2];
75+
stream_parser p(storage_ptr(), opt, parser_buffer);
76+
p.reset(std::move(sp));
77+
78+
char read_buffer[BOOST_JSON_STACK_BUFFER_SIZE / 2];
79+
while( true )
80+
{
81+
if( is.rdstate() & std::ios::eofbit )
82+
{
83+
p.finish(ec);
84+
if( ec.failed() )
85+
return nullptr;
86+
break;
87+
}
88+
89+
if( is.rdstate() != std::ios::goodbit )
90+
{
91+
BOOST_JSON_FAIL( ec, error::input_error );
92+
return nullptr;
93+
}
94+
95+
is.read(read_buffer, sizeof(read_buffer));
96+
auto const consumed = is.gcount();
97+
98+
p.write(read_buffer, consumed, ec);
99+
if( ec.failed() )
100+
return nullptr;
101+
}
102+
103+
return p.release();
104+
}
105+
106+
value
107+
parse(
108+
std::istream& is,
109+
std::error_code& ec,
110+
storage_ptr sp,
111+
parse_options const& opt)
112+
{
113+
error_code jec;
114+
value result = parse(is, jec, std::move(sp), opt);
115+
ec = jec;
116+
return result;
117+
}
118+
119+
value
120+
parse(
121+
std::istream& is,
122+
storage_ptr sp,
123+
parse_options const& opt)
124+
{
125+
error_code ec;
126+
auto jv = parse(
127+
is, ec, std::move(sp), opt);
128+
if(ec)
129+
detail::throw_system_error(ec,
130+
BOOST_CURRENT_LOCATION);
131+
return jv;
132+
}
133+
65134
BOOST_JSON_NS_END
66135

67136
#endif

include/boost/json/parse.hpp

Lines changed: 89 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ parse(
7272
parse_options const& opt = {});
7373
/** @} */
7474

75-
/** Parse a string of JSON into a @ref value.
75+
/** Return parsed JSON as a @ref value.
7676
7777
This function parses an entire string in one
7878
step to produce a complete JSON object, returned
@@ -111,6 +111,94 @@ parse(
111111
storage_ptr sp = {},
112112
parse_options const& opt = {});
113113

114+
/** Return parsed JSON as a @ref value.
115+
116+
This function reads data from an input stream and parses it to produce a
117+
complete JSON entity, returned as a @ref value. If the stream does not
118+
contain a complete serialized JSON, or contains extra non-whitespace data,
119+
an error occurs. In this case the returned value will be `null`, using the
120+
default memory resource.
121+
122+
@par Complexity
123+
Linear in the size of consumed input.
124+
125+
@par Exception Safety
126+
Basic guarantee.
127+
Calls to `memory_resource::allocate` may throw.
128+
The stream may throw as described by
129+
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
130+
131+
@return A value representing the parsed JSON,
132+
or a `null` if any error occurred.
133+
134+
@param is The stream to read from.
135+
136+
@param ec Set to the error, if any occurred.
137+
138+
@param sp The memory resource that the new value and all of its elements
139+
will use. If this parameter is omitted, the default memory resource
140+
is used.
141+
142+
@param opt The options for the parser. If this parameter is omitted, the
143+
parser will accept only standard JSON.
144+
145+
@see @ref parse_options, @ref stream_parser, @ref value::operator>>.
146+
*/
147+
/** @{ */
148+
BOOST_JSON_DECL
149+
value
150+
parse(
151+
std::istream& is,
152+
error_code& ec,
153+
storage_ptr sp = {},
154+
parse_options const& opt = {});
155+
156+
BOOST_JSON_DECL
157+
value
158+
parse(
159+
std::istream& is,
160+
std::error_code& ec,
161+
storage_ptr sp = {},
162+
parse_options const& opt = {});
163+
/** @} */
164+
165+
/** Return parsed JSON as a @ref value.
166+
167+
This function reads data from an input stream and parses it to produce a
168+
complete JSON entity, returned as a @ref value. If the stream does not
169+
contain a complete serialized JSON, or contains extra non-whitespace data,
170+
an exception is thrown.
171+
172+
@par Complexity
173+
Linear in the size of consumed input.
174+
175+
@par Exception Safety
176+
Basic guarantee.
177+
Throws @ref system_error on failed parse.
178+
Calls to `memory_resource::allocate` may throw.
179+
The stream may throw as described by
180+
[`std::ios::exceptions`](https://en.cppreference.com/w/cpp/io/basic_ios/exceptions).
181+
182+
@return A value representing the parsed JSON upon success.
183+
184+
@param is The stream to read from.
185+
186+
@param sp The memory resource that the new value and all of its elements
187+
will use. If this parameter is omitted, the default memory resource
188+
is used.
189+
190+
@param opt The options for the parser. If this parameter is omitted, the
191+
parser will accept only standard JSON.
192+
193+
@see @ref parse_options, @ref stream_parser, @ref value::operator>>.
194+
*/
195+
BOOST_JSON_DECL
196+
value
197+
parse(
198+
std::istream& is,
199+
storage_ptr sp = {},
200+
parse_options const& opt = {});
201+
114202
BOOST_JSON_NS_END
115203

116204
#endif

include/boost/json/value.hpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3343,7 +3343,12 @@ class value
33433343
33443344
This function parses JSON from an input stream into a `value`. If
33453345
parsing fails, `std::ios_base::failbit` will be set for `is` and
3346-
`jv` will be left unchanged.
3346+
`jv` will be left unchanged.<br>
3347+
3348+
Note: this operator cannot assume that the stream only contains a
3349+
single JSON document, which results in **very underwhelming
3350+
performance**. If you know that your input consists of a single
3351+
JSON document, consider using @ref parse function instead.
33473352
33483353
@return Reference to `is`.
33493354
@@ -3359,6 +3364,8 @@ class value
33593364
@param is The input stream to parse from.
33603365
33613366
@param jv The value to parse into.
3367+
3368+
@see @ref parse.
33623369
*/
33633370
BOOST_JSON_DECL
33643371
friend

test/error.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ class error_test
6060
check(condition::parse_error, error::array_too_large);
6161
check(condition::parse_error, error::key_too_large);
6262
check(condition::parse_error, error::string_too_large);
63+
check(condition::parse_error, error::input_error);
6364

6465
check(condition::pointer_parse_error, error::missing_slash);
6566
check(condition::pointer_parse_error, error::invalid_escape);

test/parse.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//
22
// Copyright (c) 2019 Vinnie Falco (vinnie.falco@gmail.com)
3+
// Copyright (c) 2022 Dmitry Arkhipov (grisumbras@yandex.ru)
34
//
45
// Distributed under the Boost Software License, Version 1.0. (See accompanying
56
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
@@ -42,6 +43,12 @@ class parse_test
4243
return;
4344
BOOST_TEST(
4445
serialize(jv) == s);
46+
47+
std::stringstream ss(s);
48+
auto jv2 = parse(ss, ec);
49+
if(! BOOST_TEST(! ec))
50+
return;
51+
BOOST_TEST( jv == jv2 );
4552
}
4653

4754
template <class ErrorCode>
@@ -52,6 +59,12 @@ class parse_test
5259
auto jv = parse(s, ec);
5360
BOOST_TEST(ec);
5461
BOOST_TEST(hasLocation(ec));
62+
63+
ec = {};
64+
std::stringstream ss(s);
65+
auto jv2 = parse(ss, ec);
66+
BOOST_TEST(ec);
67+
BOOST_TEST(hasLocation(ec));
5568
}
5669

5770
void
@@ -73,6 +86,7 @@ class parse_test
7386
{
7487
good("null");
7588
good("[1,2,3]");
89+
good("17");
7690
bad ("[1,2,3] #");
7791
bad ("555415214748364655415E2147483646");
7892
bad ("9.88874836020e-2147483640");
@@ -178,12 +192,24 @@ class parse_test
178192
BOOST_TEST(arr == array{123});
179193
}
180194

195+
void
196+
testIstream()
197+
{
198+
std::stringstream ss("null");
199+
parse(ss); // does not throw
200+
201+
ss.clear();
202+
ss.setstate(std::ios::failbit);
203+
BOOST_TEST_THROWS( parse(ss), system_error );
204+
}
205+
181206
void
182207
run()
183208
{
184209
testParse();
185210
testMemoryUsage();
186211
testIssue726();
212+
testIstream();
187213
}
188214
};
189215

0 commit comments

Comments
 (0)