-
-
Notifications
You must be signed in to change notification settings - Fork 6.9k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Optimize copy / assignment and size of json iterator
- Loading branch information
Showing
9 changed files
with
1,314 additions
and
387 deletions.
There are no files selected for viewing
367 changes: 357 additions & 10 deletions
367
include/nlohmann/detail/iterators/internal_iterator.hpp
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.