Skip to content

Commit 3b88c85

Browse files
committed
Merge bitcoin#26612: refactor: RPC: pass named argument value as string_view
545ff92 refactor: use string_view for RPC named argument values (stickies-v) 7727603 refactor: reduce unnecessary complexity in ParseNonRFCJSONValue (stickies-v) 1d02e59 test: add cases to JSON parsing (stickies-v) Pull request description: Inspired by MarcoFalke's [comment](bitcoin#26506 (comment)). Main purpose of this PR is to minimize copying (potentially large) RPC named arguments when calling `.substr()` by using `std::string_view` instead of `std::string`. Furthermore, cleans up the code by removing unnecessary complexity in `ParseNonRFCJSONValue()` (done first to avoid refactoring required to concatenate `string` and `string_view`), updates some naming and adds a few test cases. Should not introduce any behaviour change. ## Questions - ~Was there actually any merit to `ParseNonRFCJSONValue()` surrounding the value with brackets and then parsing it as an array? I don't see it, and the new approach doesn't fail any tests. Still a bit suspicious about it though.~ - Cleared up by bitcoin#26506 (review) - If there are no objections to 7727603, I think we should follow up with a PR to rename `ParseNonRFCJSONValue()` to a local `Parse()` helper function (that throws if invalid), remove it from `client.h` and merge the test coverage we currently have on `ParseNonRFCJSONValue()` with the coverage we have on `UniValue::read()`. ACKs for top commit: ryanofsky: Code review ACK 545ff92 MarcoFalke: review ACK 545ff92 📻 Tree-SHA512: b1c89fb010ac9c3054b023cac1acbba2a539a09cf39a7baffbd7f7571ee268d5a6d98701c7ac10d68a814526e8fd0fe96ac1d1fb072f272033e415b753f64a5c
2 parents a12b27a + 545ff92 commit 3b88c85

File tree

4 files changed

+31
-19
lines changed

4 files changed

+31
-19
lines changed

src/rpc/client.cpp

+16-15
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,13 @@
44
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
55

66
#include <rpc/client.h>
7+
#include <tinyformat.h>
78
#include <util/system.h>
89

910
#include <set>
1011
#include <stdint.h>
12+
#include <string>
13+
#include <string_view>
1114

1215
class CRPCConvertParam
1316
{
@@ -229,15 +232,15 @@ class CRPCConvertTable
229232
CRPCConvertTable();
230233

231234
/** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
232-
UniValue ArgToUniValue(const std::string& arg_value, const std::string& method, int param_idx)
235+
UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, int param_idx)
233236
{
234-
return members.count(std::make_pair(method, param_idx)) > 0 ? ParseNonRFCJSONValue(arg_value) : arg_value;
237+
return members.count({method, param_idx}) > 0 ? ParseNonRFCJSONValue(arg_value) : arg_value;
235238
}
236239

237240
/** Return arg_value as UniValue, and first parse it if it is a non-string parameter */
238-
UniValue ArgToUniValue(const std::string& arg_value, const std::string& method, const std::string& param_name)
241+
UniValue ArgToUniValue(std::string_view arg_value, const std::string& method, const std::string& param_name)
239242
{
240-
return membersByName.count(std::make_pair(method, param_name)) > 0 ? ParseNonRFCJSONValue(arg_value) : arg_value;
243+
return membersByName.count({method, param_name}) > 0 ? ParseNonRFCJSONValue(arg_value) : arg_value;
241244
}
242245
};
243246

@@ -254,22 +257,20 @@ static CRPCConvertTable rpcCvtTable;
254257
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
255258
* as well as objects and arrays.
256259
*/
257-
UniValue ParseNonRFCJSONValue(const std::string& strVal)
260+
UniValue ParseNonRFCJSONValue(std::string_view raw)
258261
{
259-
UniValue jVal;
260-
if (!jVal.read(std::string("[")+strVal+std::string("]")) ||
261-
!jVal.isArray() || jVal.size()!=1)
262-
throw std::runtime_error(std::string("Error parsing JSON: ") + strVal);
263-
return jVal[0];
262+
UniValue parsed;
263+
if (!parsed.read(raw)) throw std::runtime_error(tfm::format("Error parsing JSON: %s", raw));
264+
return parsed;
264265
}
265266

266267
UniValue RPCConvertValues(const std::string &strMethod, const std::vector<std::string> &strParams)
267268
{
268269
UniValue params(UniValue::VARR);
269270

270271
for (unsigned int idx = 0; idx < strParams.size(); idx++) {
271-
const std::string& strVal = strParams[idx];
272-
params.push_back(rpcCvtTable.ArgToUniValue(strVal, strMethod, idx));
272+
std::string_view value{strParams[idx]};
273+
params.push_back(rpcCvtTable.ArgToUniValue(value, strMethod, idx));
273274
}
274275

275276
return params;
@@ -280,15 +281,15 @@ UniValue RPCConvertNamedValues(const std::string &strMethod, const std::vector<s
280281
UniValue params(UniValue::VOBJ);
281282
UniValue positional_args{UniValue::VARR};
282283

283-
for (const std::string &s: strParams) {
284+
for (std::string_view s: strParams) {
284285
size_t pos = s.find('=');
285286
if (pos == std::string::npos) {
286287
positional_args.push_back(rpcCvtTable.ArgToUniValue(s, strMethod, positional_args.size()));
287288
continue;
288289
}
289290

290-
std::string name = s.substr(0, pos);
291-
std::string value = s.substr(pos+1);
291+
std::string name{s.substr(0, pos)};
292+
std::string_view value{s.substr(pos+1)};
292293

293294
// Intentionally overwrite earlier named values with later ones as a
294295
// convenience for scripts and command line users that want to merge

src/rpc/client.h

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#ifndef BITCOIN_RPC_CLIENT_H
77
#define BITCOIN_RPC_CLIENT_H
88

9+
#include <string>
10+
#include <string_view>
11+
912
#include <univalue.h>
1013

1114
/** Convert positional arguments to command-specific RPC representation */
@@ -17,6 +20,6 @@ UniValue RPCConvertNamedValues(const std::string& strMethod, const std::vector<s
1720
/** Non-RFC4627 JSON parser, accepts internal values (such as numbers, true, false, null)
1821
* as well as objects and arrays.
1922
*/
20-
UniValue ParseNonRFCJSONValue(const std::string& strVal);
23+
UniValue ParseNonRFCJSONValue(std::string_view raw);
2124

2225
#endif // BITCOIN_RPC_CLIENT_H

src/test/rpc_tests.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -289,6 +289,10 @@ BOOST_AUTO_TEST_CASE(json_parse_errors)
289289
{
290290
// Valid
291291
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0").get_real(), 1.0);
292+
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("true").get_bool(), true);
293+
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("[false]")[0].get_bool(), false);
294+
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("{\"a\": true}")["a"].get_bool(), true);
295+
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("{\"1\": \"true\"}")["1"].get_str(), "true");
292296
// Valid, with leading or trailing whitespace
293297
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue(" 1.0").get_real(), 1.0);
294298
BOOST_CHECK_EQUAL(ParseNonRFCJSONValue("1.0 ").get_real(), 1.0);
@@ -301,6 +305,11 @@ BOOST_AUTO_TEST_CASE(json_parse_errors)
301305
// Invalid, trailing garbage
302306
BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0sds"), std::runtime_error);
303307
BOOST_CHECK_THROW(ParseNonRFCJSONValue("1.0]"), std::runtime_error);
308+
// Invalid, keys have to be names
309+
BOOST_CHECK_THROW(ParseNonRFCJSONValue("{1: \"true\"}"), std::runtime_error);
310+
BOOST_CHECK_THROW(ParseNonRFCJSONValue("{true: 1}"), std::runtime_error);
311+
BOOST_CHECK_THROW(ParseNonRFCJSONValue("{[1]: 1}"), std::runtime_error);
312+
BOOST_CHECK_THROW(ParseNonRFCJSONValue("{{\"a\": \"a\"}: 1}"), std::runtime_error);
304313
// BTC addresses should fail parsing
305314
BOOST_CHECK_THROW(ParseNonRFCJSONValue("175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W"), std::runtime_error);
306315
BOOST_CHECK_THROW(ParseNonRFCJSONValue("3J98t1WpEZ73CNmQviecrnyiWrnqRhWNL"), std::runtime_error);

src/univalue/include/univalue.h

+2-3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include <map>
1313
#include <stdexcept>
1414
#include <string>
15+
#include <string_view>
1516
#include <type_traits>
1617
#include <vector>
1718

@@ -95,9 +96,7 @@ class UniValue {
9596

9697
bool read(const char *raw, size_t len);
9798
bool read(const char *raw) { return read(raw, strlen(raw)); }
98-
bool read(const std::string& rawStr) {
99-
return read(rawStr.data(), rawStr.size());
100-
}
99+
bool read(std::string_view raw) { return read(raw.data(), raw.size()); }
101100

102101
private:
103102
UniValue::VType typ;

0 commit comments

Comments
 (0)