Skip to content

Commit

Permalink
Optimize copy / assignment and size of json iterator
Browse files Browse the repository at this point in the history
  • Loading branch information
sjanel committed Apr 28, 2022
1 parent a6ee8bf commit fa038d1
Show file tree
Hide file tree
Showing 9 changed files with 1,314 additions and 387 deletions.
367 changes: 357 additions & 10 deletions include/nlohmann/detail/iterators/internal_iterator.hpp
Original file line number Diff line number Diff line change
@@ -1,25 +1,372 @@
#pragma once

#include <algorithm>
#include <cstdint>
#include <type_traits>
#include <utility>

#include <nlohmann/detail/iterators/primitive_iterator.hpp>
#include <nlohmann/detail/macro_scope.hpp>
#include <nlohmann/detail/meta/memory.hpp>
#include <nlohmann/detail/meta/type_traits.hpp>
#include <nlohmann/detail/value_t.hpp>

namespace nlohmann
{
namespace detail
{

template<class ObjectIterator, class ArrayIterator>
class internal_iterator_base
{
public:
enum class iterator_type : std::uint8_t
{
object,
array,
primitive,
none
};

ObjectIterator& object_iterator()
{
return *reinterpret_cast<ObjectIterator*>(&m_data);
}
const ObjectIterator& object_iterator() const
{
return *reinterpret_cast<const ObjectIterator*>(&m_data);
}

ArrayIterator& array_iterator()
{
return *reinterpret_cast<ArrayIterator*>(&m_data);
}
const ArrayIterator& array_iterator() const
{
return *reinterpret_cast<const ArrayIterator*>(&m_data);
}

primitive_iterator_t& primitive_iterator()
{
return *reinterpret_cast<primitive_iterator_t*>(&m_data);
}
const primitive_iterator_t& primitive_iterator() const
{
return *reinterpret_cast<const primitive_iterator_t*>(&m_data);
}

private:
template<class, class, bool>
friend class internal_iterator_impl;

/// Default constructor provided for optimization purposes,
/// as it does not initialize the underlying array, should be called internally only.
internal_iterator_base() noexcept = default;

explicit internal_iterator_base(iterator_type it_type) noexcept : m_data()
{
switch (it_type)
{
case iterator_type::object:
::new (&object_iterator()) ObjectIterator();
break;
case iterator_type::array:
::new (&array_iterator()) ArrayIterator();
break;
case iterator_type::primitive:
::new (&primitive_iterator()) primitive_iterator_t();
break;
case iterator_type::none:
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}

static inline iterator_type iterator_type_from_value(value_t v_type) noexcept
{
switch (v_type)
{
case value_t::object:
{
return iterator_type::object;
}

case value_t::array:
{
return iterator_type::array;
}

case value_t::null:
case value_t::string:
case value_t::boolean:
case value_t::number_integer:
case value_t::number_unsigned:
case value_t::number_float:
case value_t::binary:
case value_t::discarded:
default:
{
return iterator_type::primitive;
}
}
}

struct internal_data_t
{
internal_data_t() noexcept = default;

#ifdef JSON_HAS_CPP_14
// NOLINTNEXTLINE
alignas(std::max({alignof(ObjectIterator), alignof(ArrayIterator), alignof(primitive_iterator_t)})) std::uint8_t m_t[std::max({sizeof(ObjectIterator), sizeof(ArrayIterator), sizeof(primitive_iterator_t)})];
#else
// Below code is horrible, but it's the only way to make gcc 4.8 compile
alignas(alignof(ObjectIterator) < alignof(ArrayIterator) ? (alignof(ArrayIterator) < alignof(primitive_iterator_t)
? alignof(primitive_iterator_t)
: alignof(ArrayIterator))
: (alignof(ObjectIterator) < alignof(primitive_iterator_t)
? alignof(primitive_iterator_t)
// NOLINTNEXTLINE
: alignof(ObjectIterator))) std::uint8_t m_t[sizeof(ObjectIterator) < sizeof(ArrayIterator) ? (sizeof(ArrayIterator) < sizeof(primitive_iterator_t)
? sizeof(primitive_iterator_t)
: sizeof(ArrayIterator))
: (sizeof(ObjectIterator) < sizeof(primitive_iterator_t)
? sizeof(primitive_iterator_t)
: sizeof(ObjectIterator))];
#endif
};
internal_data_t m_data;
};

/*!
@brief an iterator value
@brief Implementation of internal_iterator for non trivially copyable types
*/
template<class ObjectIterator, class ArrayIterator, bool trivially_copyable>
class internal_iterator_impl : public internal_iterator_base<ObjectIterator, ArrayIterator>
{
private:
using base_t = internal_iterator_base<ObjectIterator, ArrayIterator>;

public:
internal_iterator_impl() noexcept
: base_t(base_t::iterator_type::none)
, m_it_type(base_t::iterator_type::none)
{
}

explicit internal_iterator_impl(value_t v_type) noexcept
: base_t(base_t::iterator_type_from_value(v_type))
, m_it_type(base_t::iterator_type_from_value(v_type))
{
}

internal_iterator_impl(const internal_iterator_impl& o) noexcept
{
copy_construct(o);
}

@note This structure could easily be a union, but MSVC currently does not allow
unions members with complex constructors, see https://github.com/nlohmann/json/pull/105.
internal_iterator_impl(internal_iterator_impl&& o) noexcept
{
move_construct(std::move(o));
}

internal_iterator_impl& operator=(const internal_iterator_impl& o) noexcept
{
if (&o != this)
{
if (o.m_it_type == m_it_type)
{
switch (m_it_type)
{
case base_t::iterator_type::object:
this->object_iterator() = o.object_iterator();
break;
case base_t::iterator_type::array:
this->array_iterator() = o.array_iterator();
break;
case base_t::iterator_type::primitive:
this->primitive_iterator() = o.primitive_iterator();
break;
case base_t::iterator_type::none:
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
else
{
destroy_it();
copy_construct(o);
}
}
return *this;
}

internal_iterator_impl& operator=(internal_iterator_impl&& o) noexcept
{
if (&o != this)
{
if (o.m_it_type == m_it_type)
{
switch (m_it_type)
{
case base_t::iterator_type::object:
this->object_iterator() = std::move(o.object_iterator());
break;
case base_t::iterator_type::array:
this->array_iterator() = std::move(o.array_iterator());
break;
case base_t::iterator_type::primitive:
this->primitive_iterator() = std::move(o.primitive_iterator());
break;
case base_t::iterator_type::none:
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}
else
{
destroy_it();
move_construct(std::move(o));
}
}
return *this;
}

~internal_iterator_impl()
{
destroy_it();
}

template<class ObjIt>
void set_object_iterator(ObjIt&& it) noexcept
{
JSON_ASSERT(m_it_type == base_t::iterator_type::object);
this->object_iterator() = std::forward<ObjIt>(it);
}

template<class ArrIt>
void set_array_iterator(ArrIt&& it) noexcept
{
JSON_ASSERT(m_it_type == base_t::iterator_type::array);
this->array_iterator() = std::forward<ArrIt>(it);
}

private:
void destroy_it() noexcept
{
// We call the destructor of the underlying initialized object
switch (m_it_type)
{
case base_t::iterator_type::object:
destroy_at(&this->object_iterator());
break;
case base_t::iterator_type::array:
destroy_at(&this->array_iterator());
break;
case base_t::iterator_type::primitive:
destroy_at(&this->primitive_iterator());
break;
case base_t::iterator_type::none:
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}

void copy_construct(const internal_iterator_impl& o) noexcept
{
m_it_type = o.m_it_type;
switch (m_it_type)
{
case base_t::iterator_type::object:
::new (&this->object_iterator()) ObjectIterator(o.object_iterator());
break;
case base_t::iterator_type::array:
::new (&this->array_iterator()) ArrayIterator(o.array_iterator());
break;
case base_t::iterator_type::primitive:
::new (&this->primitive_iterator()) primitive_iterator_t(o.primitive_iterator());
break;
case base_t::iterator_type::none:
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}

void move_construct(internal_iterator_impl&& o) noexcept
{
m_it_type = o.m_it_type;
switch (m_it_type)
{
case base_t::iterator_type::object:
::new (&this->object_iterator()) ObjectIterator(std::move(o.object_iterator()));
break;
case base_t::iterator_type::array:
::new (&this->array_iterator()) ArrayIterator(std::move(o.array_iterator()));
break;
case base_t::iterator_type::primitive:
::new (&this->primitive_iterator()) primitive_iterator_t(std::move(o.primitive_iterator()));
break;
case base_t::iterator_type::none:
break;
default: // LCOV_EXCL_LINE
JSON_ASSERT(false); // NOLINT(cert-dcl03-c,hicpp-static-assert,misc-static-assert) LCOV_EXCL_LINE
}
}

typename base_t::iterator_type m_it_type;
};

/*!
@brief Implementation of internal_iterator for trivially copyable types
*/
template<typename BasicJsonType> struct internal_iterator
template<class ObjectIterator, class ArrayIterator>
class internal_iterator_impl<ObjectIterator, ArrayIterator, true> :
public internal_iterator_base<ObjectIterator, ArrayIterator>
{
/// iterator for JSON objects
typename BasicJsonType::object_t::iterator object_iterator {};
/// iterator for JSON arrays
typename BasicJsonType::array_t::iterator array_iterator {};
/// generic iterator for all other types
primitive_iterator_t primitive_iterator {};
private:
using base_t = internal_iterator_base<ObjectIterator, ArrayIterator>;

public:
internal_iterator_impl() noexcept
: base_t(base_t::iterator_type::none)
{
}

explicit internal_iterator_impl(value_t v_type) noexcept
: base_t(base_t::iterator_type_from_value(v_type))
{
}

template<class ObjIt>
void set_object_iterator(ObjIt&& it) noexcept
{
this->object_iterator() = std::forward<ObjIt>(it);
}

template<class ArrIt>
void set_array_iterator(ArrIt&& it) noexcept
{
this->array_iterator() = std::forward<ArrIt>(it);
}
};

/*!
@brief an iterator value
*/
template<class ObjectIterator, class ArrayIterator>
using internal_iterator = internal_iterator_impl < ObjectIterator, ArrayIterator,
#if defined(__clang__) || !defined(JSON_HEDLEY_GNUC_VERSION) || JSON_HEDLEY_GNUC_VERSION_CHECK(5, 0, 0)
is_trivially_copyable<ObjectIterator>::value &&
is_trivially_copyable<ArrayIterator>::value
#else
// gcc < v5 is not able to compute these traits.
false
#endif
>;

} // namespace detail
} // namespace nlohmann
Loading

0 comments on commit fa038d1

Please sign in to comment.