Skip to content

Commit c432951

Browse files
committed
do not reserve preemptively in object
1 parent 4700500 commit c432951

File tree

4 files changed

+72
-39
lines changed

4 files changed

+72
-39
lines changed

include/boost/json/impl/object.hpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ insert(P&& p) ->
376376
{
377377
key_value_pair v(
378378
std::forward<P>(p), sp_);
379-
return insert_impl(pilfer(v));
379+
return emplace_impl( v.key(), pilfer(v) );
380380
}
381381

382382
template<class M>
@@ -386,18 +386,14 @@ insert_or_assign(
386386
string_view key, M&& m) ->
387387
std::pair<iterator, bool>
388388
{
389-
reserve(size() + 1);
390-
auto const result = detail::find_in_object(*this, key);
391-
if(result.first)
389+
std::pair<iterator, bool> result = emplace_impl(
390+
key, key, static_cast<M&&>(m) );
391+
if( !result.second )
392392
{
393-
value(std::forward<M>(m),
394-
sp_).swap(result.first->value());
395-
return { result.first, false };
393+
value(static_cast<M>(m), sp_).swap(
394+
result.first->value());
396395
}
397-
key_value_pair kv(key,
398-
std::forward<M>(m), sp_);
399-
return { insert_impl(pilfer(kv),
400-
result.second), true };
396+
return result;
401397
}
402398

403399
template<class Arg>
@@ -408,14 +404,7 @@ emplace(
408404
Arg&& arg) ->
409405
std::pair<iterator, bool>
410406
{
411-
reserve(size() + 1);
412-
auto const result = detail::find_in_object(*this, key);
413-
if(result.first)
414-
return { result.first, false };
415-
key_value_pair kv(key,
416-
std::forward<Arg>(arg), sp_);
417-
return { insert_impl(pilfer(kv),
418-
result.second), true };
407+
return emplace_impl( key, key, static_cast<Arg&&>(arg) );
419408
}
420409

421410
//----------------------------------------------------------
@@ -512,6 +501,40 @@ insert(
512501
r.commit();
513502
}
514503

504+
template< class... Args >
505+
std::pair<object::iterator, bool>
506+
object::
507+
emplace_impl( string_view key, Args&& ... args )
508+
{
509+
std::pair<iterator, std::size_t> search_result(nullptr, 0);
510+
if( !empty() )
511+
{
512+
search_result = detail::find_in_object(*this, key);
513+
if( search_result.first )
514+
return { search_result.first, false };
515+
}
516+
517+
// we create the new value before reserving, in case it is a reference to
518+
// a subobject of the current object
519+
key_value_pair kv( static_cast<Args&&>(args)..., sp_ );
520+
// the key might get deallocated too
521+
key = kv.key();
522+
523+
std::size_t const old_capacity = capacity();
524+
reserve(size() + 1);
525+
if( (empty() && capacity() > detail::small_object_size_)
526+
|| (capacity() != old_capacity) )
527+
search_result.second = detail::digest(
528+
key.begin(), key.end(), t_->salt);
529+
530+
BOOST_ASSERT(
531+
t_->is_small() ||
532+
(search_result.second ==
533+
detail::digest(key.begin(), key.end(), t_->salt)) );
534+
535+
return { insert_impl(pilfer(kv), search_result.second), true };
536+
}
537+
515538
//----------------------------------------------------------
516539

517540
namespace detail {

include/boost/json/impl/object.ipp

Lines changed: 0 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -683,23 +683,6 @@ if_contains(
683683
//
684684
//----------------------------------------------------------
685685

686-
auto
687-
object::
688-
insert_impl(
689-
pilfered<key_value_pair> p) ->
690-
std::pair<iterator, bool>
691-
{
692-
// caller is responsible
693-
// for preventing aliasing.
694-
reserve(size() + 1);
695-
auto const result =
696-
detail::find_in_object(*this, p.get().key());
697-
if(result.first)
698-
return { result.first, false };
699-
return { insert_impl(
700-
p, result.second), true };
701-
}
702-
703686
key_value_pair*
704687
object::
705688
insert_impl(

include/boost/json/object.hpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1596,10 +1596,9 @@ class object
15961596
InputIt last,
15971597
std::forward_iterator_tag);
15981598

1599-
BOOST_JSON_DECL
1599+
template< class... Args >
16001600
std::pair<iterator, bool>
1601-
insert_impl(
1602-
pilfered<key_value_pair> p);
1601+
emplace_impl(string_view key, Args&& ... args );
16031602

16041603
BOOST_JSON_DECL
16051604
key_value_pair*

test/object.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1595,6 +1595,33 @@ class object_test
15951595
object({{"b",2}, {"c",3}})));
15961596
}
15971597

1598+
void
1599+
testStrongGurantee()
1600+
{
1601+
// We used to preemptively reserve storage even if we don't add a new
1602+
// element. That violated strong guarantee requirement. This test
1603+
// checks we don't do that any more.
1604+
1605+
object o;
1606+
o.reserve(100);
1607+
std::size_t const capacity = o.capacity();
1608+
for( std::size_t i = 0; i < o.capacity() ; ++i )
1609+
o.emplace( std::to_string(i), i );
1610+
BOOST_ASSERT( capacity == o.capacity() );
1611+
1612+
BOOST_TEST( !o.emplace("0", 0).second );
1613+
BOOST_TEST( capacity == o.capacity() );
1614+
1615+
BOOST_TEST( !o.insert_or_assign("0", 0).second );
1616+
BOOST_TEST( capacity == o.capacity() );
1617+
1618+
o["0"] = 0;
1619+
BOOST_TEST( capacity == o.capacity() );
1620+
1621+
o.insert( key_value_pair("0", nullptr) );
1622+
BOOST_TEST( capacity == o.capacity() );
1623+
}
1624+
15981625
void
15991626
run()
16001627
{
@@ -1610,6 +1637,7 @@ class object_test
16101637
testEquality();
16111638
testAllocation();
16121639
testHash();
1640+
testStrongGurantee();
16131641
}
16141642
};
16151643

0 commit comments

Comments
 (0)