Skip to content

Commit

Permalink
refactor into one loop
Browse files Browse the repository at this point in the history
  • Loading branch information
grisumbras committed Nov 8, 2022
1 parent d05bc04 commit f48cc9e
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 79 deletions.
24 changes: 24 additions & 0 deletions include/boost/json/detail/value.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,30 @@ struct access
{
return e.next_;
}

template< class Object >
static
index_t&
bucket(Object& obj, string_view key) noexcept
{
return obj.t_->bucket( key );
}

template< class Object >
static
void
grow_size(Object& obj) noexcept
{
++obj.t_->size;
}

template< class Object >
static constexpr
index_t
null_index(Object const&) noexcept
{
return Object::null_index_;
}
};

} // detail
Expand Down
200 changes: 121 additions & 79 deletions include/boost/json/impl/object.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,126 @@ find_in_object<string_view>(
object const& obj,
string_view key) noexcept;

// returns pointer to duplicate element
template< bool SmallTable >
key_value_pair*
add_to_bucket( object& obj, key_value_pair* kv ) noexcept
{
return find_in_object( obj, kv->key() ).first;
}

template<>
key_value_pair*
add_to_bucket< false >( object& obj, key_value_pair* kv )
noexcept
{
auto& head = access::bucket( obj, kv->key() );
auto i = head;
while( true )
{
if(i == access::null_index(obj) )
{
// end of bucket
access::next(*kv) = head;
head = static_cast< access::index_t >( kv - obj.begin() );
return nullptr;
}

auto& v = obj.begin()[i];
if( v.key() != kv->key() )
{
i = access::next(v);
continue;
}

access::next(*kv) = access::next(v);
return &v;
}
}

template< bool Ignore >
std::integral_constant<bool, Ignore>
handle_duplicate( key_value_pair& new_kv, key_value_pair& old_kv ) noexcept
{
// don't bother to check if
// storage deallocate is trivial
old_kv.~key_value_pair();
// trivial relocate
std::memcpy( static_cast<void*>(&old_kv), &new_kv, sizeof(old_kv) );
return {};
}

template<>
std::integral_constant<bool, false>
handle_duplicate< false >( key_value_pair&, key_value_pair& ) noexcept
{
return {};
}

template< class SmallTable, class IgnoreDuplicates >
void
unchecked_init_impl(
object& obj, unchecked_object& uo, SmallTable, IgnoreDuplicates )
{
// insert all elements, either keeping the last of any duplicate keys, or
// aborting insertion, depending on whether uo.ignore_duplicates is false.
auto dest = obj.begin();
for( ; uo.size(); uo.pop_front() )
{
auto src = uo.front();
access::construct_key_value_pair(
dest, pilfer(src[0]), pilfer(src[1]));

auto const duplicate = add_to_bucket< SmallTable::value >( obj, dest );
if( !duplicate )
{
++dest;
access::grow_size(obj);
continue;
}
if( !handle_duplicate< IgnoreDuplicates::value >(*dest, *duplicate) )
{
dest->~key_value_pair();
return;
}
}
}

template< class SmallTable >
struct unchecked_init_helper2
{
object& obj;
unchecked_object& uo;

template< class IgnoreDuplicates >
void operator()(IgnoreDuplicates) const
{
unchecked_init_impl( obj, uo, SmallTable(), IgnoreDuplicates() );
}
};

struct unchecked_init_helper1
{
object& obj;
unchecked_object& uo;

template< class SmallTable >
void operator()(SmallTable) const
{
mp11::mp_with_index<2>(
uo.ignore_duplicate_keys(),
unchecked_init_helper2< SmallTable >{obj, uo} );
}
};

void
initialize_from_unchecked(object& obj, unchecked_object& uo, bool small_table)
{
mp11::mp_with_index<2>(
small_table,
unchecked_init_helper1{obj, uo});
}

} // namespace detail

//----------------------------------------------------------
Expand Down Expand Up @@ -212,85 +332,7 @@ object(detail::unchecked_object& uo)
uo.size(), 0, sp_);
t_->size = 0;

// insert all elements, keeping
// the last of any duplicate keys, unless uo.ignore_duplicates is false.
auto dest = begin();
if(t_->is_small())
{
for( ; uo.size(); uo.pop_front() )
{
auto src = uo.front();
access::construct_key_value_pair(
dest, pilfer(src[0]), pilfer(src[1]));
auto result = detail::find_in_object(*this, dest->key());
if(! result.first)
{
++dest;
++t_->size;
continue;
}
// handle duplicate
if( !uo.ignore_duplicate_keys() )
{
dest->~key_value_pair();
return;
}
auto& v = *result.first;
// don't bother to check if
// storage deallocate is trivial
v.~key_value_pair();
// trivial relocate
std::memcpy(
static_cast<void*>(&v),
dest, sizeof(v));
}
return;
}
for( ; uo.size() ; uo.pop_front() )
{
auto src = uo.front();
access::construct_key_value_pair(
dest, pilfer(src[0]), pilfer(src[1]));
auto& head = t_->bucket(dest->key());
auto i = head;
for(;;)
{
if(i == null_index_)
{
// end of bucket
access::next(
*dest) = head;
head = static_cast<index_t>(
dest - begin());
++dest;
++t_->size;
break;
}
auto& v = (*t_)[i];
if(v.key() != dest->key())
{
i = access::next(v);
continue;
}

// handle duplicate
if( !uo.ignore_duplicate_keys() )
{
dest->~key_value_pair();
return;
}
access::next(*dest) =
access::next(v);
// don't bother to check if
// storage deallocate is trivial
v.~key_value_pair();
// trivial relocate
std::memcpy(
static_cast<void*>(&v),
dest, sizeof(v));
break;
}
}
detail::initialize_from_unchecked( *this, uo, t_->is_small() );
}

object::
Expand Down
3 changes: 3 additions & 0 deletions include/boost/json/object.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1552,7 +1552,10 @@ class object
operator<<(
std::ostream& os,
object const& obj);

private:
friend struct detail::access;

#ifndef BOOST_JSON_DOCS
// VFALCO friending a detail function makes it public
template<class CharRange>
Expand Down

0 comments on commit f48cc9e

Please sign in to comment.