Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

option to check duplicate object keys #801

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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() )
grisumbras marked this conversation as resolved.
Show resolved Hide resolved
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
38 changes: 38 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,37 @@ 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 )
{
BOOST_ASSERT( first->is_string() );
value* other = first + 2;
while( true )
{
BOOST_ASSERT( other->is_string() );
if( first->get_string() == other->get_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 +498,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