// Copyright (C) 2024 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QPROTOBUFREPEATEDITERATOR_H #define QPROTOBUFREPEATEDITERATOR_H #include #include #include #include #include QT_BEGIN_NAMESPACE class QProtobufMessage; class QProtobufRepeatedIterator; namespace QtProtobufPrivate { template = true> class ListIterator { public: ~ListIterator() = default; bool hasNext() const noexcept { return m_it != m_list->end(); } QProtobufMessage *next() { Q_ASSERT(m_it != m_list->end()); return &*m_it++; } QProtobufMessage *addNext() { return &m_list->emplace_back(); } /* protobuf allows having elements in the repeated fields that are failed to deserialize */ void push() noexcept { } private: Q_DISABLE_COPY_MOVE(ListIterator) friend class QT_PREPEND_NAMESPACE(QProtobufRepeatedIterator); explicit ListIterator(QList &list) : m_list(&list), m_it(m_list->begin()) { } QList *m_list = nullptr; typename QList::Iterator m_it; }; template = true> class MapIterator { using MapEntry = QProtobufMapEntry; static_assert(!std::is_pointer_v, "Map key must not be message"); public: ~MapIterator() { delete m_mapEntry; } bool hasNext() const noexcept { return m_it != m_hash->end(); } QProtobufMessage *next() { Q_ASSERT(m_it != m_hash->end()); m_mapEntry->setKey(m_it.key()); if constexpr (std::is_pointer_v) m_mapEntry->setValue(&m_it.value()); else m_mapEntry->setValue(m_it.value()); ++m_it; return m_mapEntry; } QProtobufMessage *addNext() { m_mapEntry->setKey({}); m_mapEntry->setValue({}); return m_mapEntry; } void push() { auto it = m_hash->emplace(m_mapEntry->key()); if constexpr (std::is_pointer_v) *it = std::move(*m_mapEntry->value()); else *it = m_mapEntry->value(); } private: Q_DISABLE_COPY_MOVE(MapIterator) friend class QT_PREPEND_NAMESPACE(QProtobufRepeatedIterator); explicit MapIterator(QHash &hash) : m_hash(&hash), m_it(m_hash->begin()), m_mapEntry(new MapEntry) { } QHash *m_hash = nullptr; typename QHash::Iterator m_it; MapEntry *m_mapEntry; }; } class QProtobufRepeatedIterator { enum class Operation { HasNext, Next, AddNext, Push, Deleter }; using ImplFn = void (*)(Operation op, void *data, void **args, void *ret); template static constexpr ImplFn MakeImpl() { return [](Operation op, void *data, void **, void *ret) { switch (op) { case Operation::HasNext: *static_cast(ret) = static_cast(data)->hasNext(); break; case Operation::Next: *static_cast(ret) = static_cast(data)->next(); break; case Operation::AddNext: *static_cast(ret) = static_cast(data)->addNext(); break; case Operation::Push: static_cast(data)->push(); break; case Operation::Deleter: static_cast(data)->~T(); break; } }; } public: QProtobufRepeatedIterator() = default; ~QProtobufRepeatedIterator() { if (m_impl) m_impl(Operation::Deleter, &m_data, nullptr, nullptr); } QProtobufRepeatedIterator(QProtobufRepeatedIterator &&other) noexcept : m_data(std::exchange(other.m_data, {})), m_impl(std::exchange(other.m_impl, nullptr)) { } QT_MOVE_ASSIGNMENT_OPERATOR_IMPL_VIA_PURE_SWAP(QProtobufRepeatedIterator) void swap(QProtobufRepeatedIterator &other) noexcept { std::swap(m_data, other.m_data); qt_ptr_swap(m_impl, other.m_impl); }; [[nodiscard]] bool isValid() const noexcept { return m_impl != nullptr; } [[nodiscard]] bool hasNext() const noexcept { bool ret = false; if (m_impl) m_impl(Operation::HasNext, &m_data, nullptr, &ret); return ret; } [[nodiscard]] QProtobufMessage *next() { Q_ASSERT(m_impl); QProtobufMessage *ret; m_impl(Operation::Next, &m_data, nullptr, &ret); return ret; } [[nodiscard]] QProtobufMessage *addNext() { Q_ASSERT(m_impl); QProtobufMessage *ret; m_impl(Operation::AddNext, &m_data, nullptr, &ret); return ret; } void push() { Q_ASSERT(m_impl); m_impl(Operation::Push, &m_data, nullptr, nullptr); } template static QProtobufRepeatedIterator fromList(QList &list) { return QProtobufRepeatedIterator(list); } template static QProtobufRepeatedIterator fromHash(QHash &hash) { return QProtobufRepeatedIterator(hash); } private: Q_DISABLE_COPY(QProtobufRepeatedIterator) template> explicit QProtobufRepeatedIterator(QHash &data) noexcept : m_impl(MakeImpl()) { ::new (static_cast(&m_data)) Iterator(data); } template> explicit QProtobufRepeatedIterator(QList &data) noexcept : m_impl(MakeImpl()) { ::new (static_cast(&m_data)) Iterator(data); } mutable struct { alignas(sizeof(void *)) unsigned char data[sizeof(void *) * 4]; } m_data; ImplFn m_impl = nullptr; static_assert(sizeof(QHash::const_iterator) <= sizeof(m_data)); static_assert(alignof(decltype(m_data)) == alignof(void *)); }; QT_END_NAMESPACE #endif // QPROTOBUFREPEATEDITERATOR_H