Skip to content

Commit

Permalink
option to check duplicate object keys
Browse files Browse the repository at this point in the history
  • Loading branch information
grisumbras committed Nov 6, 2022
1 parent 3404427 commit eff5301
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 13 deletions.
1 change: 1 addition & 0 deletions include/boost/json/detail/handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ struct handler
max_string_size = string::max_size();

value_stack st;
bool ignore_duplicate_keys = true;

template<class... Args>
explicit
Expand Down
7 changes: 6 additions & 1 deletion include/boost/json/detail/impl/handler.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,13 @@ bool
handler::
on_object_end(
std::size_t n,
error_code&)
error_code& ec)
{
if( !ignore_duplicate_keys )
ec = st.check_duplicates(n);
if( ec.failed() )
return false;

st.push_object(n);
return true;
}
Expand Down
3 changes: 3 additions & 0 deletions include/boost/json/error.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,9 @@ enum class error
/// error occured when trying to read input
input_error,

/// duplicate object key
duplicate_key,

//
// generic errors
//
Expand Down
2 changes: 2 additions & 0 deletions include/boost/json/impl/error.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ case error::array_too_large: return "array too large";
case error::key_too_large: return "key too large";
case error::string_too_large: return "string too large";
case error::input_error: return "input error";
case error::duplicate_key: return "duplicate key";

case error::exception: return "got exception";
case error::test_failure: return "test failure";
Expand Down Expand Up @@ -93,6 +94,7 @@ case error::array_too_large:
case error::key_too_large:
case error::string_too_large:
case error::input_error:
case error::duplicate_key:
return condition::parse_error;

case error::missing_slash:
Expand Down
11 changes: 5 additions & 6 deletions include/boost/json/impl/parser.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,19 @@ parser(
size)
{
reset();
p_.handler().ignore_duplicate_keys = opt.ignore_duplicate_keys;
}

parser::
parser(
storage_ptr sp,
parse_options const& opt) noexcept
: p_(
opt,
: parser(
std::move(sp),
nullptr,
opt,
static_cast<unsigned char*>(nullptr),
0)
{
reset();
}
{ }

void
parser::
Expand Down
11 changes: 5 additions & 6 deletions include/boost/json/impl/stream_parser.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -32,20 +32,19 @@ stream_parser(
size)
{
reset();
p_.handler().ignore_duplicate_keys = opt.ignore_duplicate_keys;
}

stream_parser::
stream_parser(
storage_ptr sp,
parse_options const& opt) noexcept
: p_(
opt,
: stream_parser(
std::move(sp),
nullptr,
opt,
static_cast<unsigned char*>(nullptr),
0)
{
reset();
}
{ }

void
stream_parser::
Expand Down
36 changes: 36 additions & 0 deletions include/boost/json/impl/value_stack.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,35 @@ has_chars()
return chars_ != 0;
}

error_code
value_stack::
stack::
check_duplicates(std::size_t n)
{
error_code ec;

for( value* first = top_ - 2 * n; first != top_; first += 2 )
{
value* other = first + 2;
while( true )
{
if( first->as_string() == other->as_string() )
{
BOOST_JSON_FAIL( ec, error::duplicate_key );
goto before_return;
}

if( other == top_ )
break;

other += 2;
}
}

before_return:
return ec;
}

//--------------------------------------

// destroy the values but
Expand Down Expand Up @@ -467,6 +496,13 @@ push_null()
st_.push(nullptr, sp_);
}

error_code
value_stack::
check_duplicates(std::size_t n)
{
return st_.check_duplicates(n);
}

BOOST_JSON_NS_END

#endif
14 changes: 14 additions & 0 deletions include/boost/json/parse_options.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,20 @@ struct parse_options
@ref stream_parser.
*/
bool allow_invalid_utf8 = false;


/** Unique keys restriction setting
Forbid duplicate keys to appear in objects.
@note Since @ref basic_parser doesn't store parsed elements directly,
this option has to be taken account by implementers of handlers.
@see
@ref basic_parser,
@ref stream_parser.
*/
bool ignore_duplicate_keys = true;
};

BOOST_JSON_NS_END
Expand Down
5 changes: 5 additions & 0 deletions include/boost/json/value_stack.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ class value_stack
inline void run_dtors(bool b) noexcept;
inline std::size_t size() const noexcept;
inline bool has_chars();
inline error_code check_duplicates(std::size_t n);

inline void clear() noexcept;
inline void maybe_grow();
Expand Down Expand Up @@ -501,6 +502,10 @@ class value_stack
BOOST_JSON_DECL
void
push_null();

BOOST_JSON_DECL
error_code
check_duplicates(std::size_t n);
};

BOOST_JSON_NS_END
Expand Down
1 change: 1 addition & 0 deletions test/error.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ class error_test
check(condition::parse_error, error::key_too_large);
check(condition::parse_error, error::string_too_large);
check(condition::parse_error, error::input_error);
check(condition::parse_error, error::duplicate_key);

check(condition::pointer_parse_error, error::missing_slash);
check(condition::pointer_parse_error, error::invalid_escape);
Expand Down
15 changes: 15 additions & 0 deletions test/parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,28 @@ class parse_test
BOOST_TEST_THROWS( parse(ss), system_error );
}

void
testDuplicates()
{
value jv = parse( R"( {"a": 1, "a": 2} )" );
BOOST_TEST( jv.as_object().size() == 1 );

parse_options opt;

error_code ec;
opt.ignore_duplicate_keys = false;
jv = parse( R"( {"a": 1, "a": 2} )", ec, {}, opt );
BOOST_TEST( ec == error::duplicate_key );
}

void
run()
{
testParse();
testMemoryUsage();
testIssue726();
testIstream();
testDuplicates();
}
};

Expand Down

0 comments on commit eff5301

Please sign in to comment.