QSequentialIterableImpl: support append

Task-number: QTBUG-80916
Change-Id: I87e74da0ce454e56b5fe94d9db3693a587d35edf
Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
Reviewed-by: Shawn Rutledge <shawn.rutledge@qt.io>
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
bb10
Fabian Kosmale 2020-01-02 13:27:10 +01:00
parent b31852c4ca
commit a4fb128b73
2 changed files with 104 additions and 8 deletions

View File

@ -977,6 +977,30 @@ enum IteratorCapability
RandomAccessCapability = 4
};
enum ContainerCapability
{
ContainerIsAppendable = 1
};
template<typename Container, typename T = void>
struct ContainerCapabilitiesImpl
{
enum {ContainerCapabilities = 0};
using appendFunction = void(*)(const void *container, const void *newElement);
static constexpr const appendFunction appendImpl = nullptr;
};
template<typename Container>
struct ContainerCapabilitiesImpl<Container, decltype(std::declval<Container>().push_back(std::declval<typename Container::value_type>()))>
{
enum {ContainerCapabilities = ContainerIsAppendable};
// The code below invokes undefined behavior if and only if the pointer passed into QSequentialIterableImpl
// pointed to a const object to begin with
static void appendImpl(const void *container, const void *value)
{ static_cast<Container *>(const_cast<void *>(container))->push_back(*static_cast<const typename Container::value_type *>(value)); }
};
template<typename T, typename Category = typename std::iterator_traits<typename T::const_iterator>::iterator_category>
struct CapabilitiesImpl;
@ -1012,6 +1036,12 @@ template<typename T>
struct ContainerAPI<std::list<T> > : CapabilitiesImpl<std::list<T> >
{ static int size(const std::list<T> *t) { return int(t->size()); } };
/*
revision 0: _iteratorCapabilities is simply a uint, where the bits at _revision were never set
revision 1: _iteratorCapabilties is treated as a bitfield, the remaining bits are used to introduce
_revision, _containerCapabilities and _unused. The latter contains 21 bits that are
not used yet
*/
class QSequentialIterableImpl
{
public:
@ -1020,19 +1050,37 @@ public:
int _metaType_id;
uint _metaType_flags;
uint _iteratorCapabilities;
// Iterator capabilities looks actually like
// uint _iteratorCapabilities:4;
// uint _revision:3;
// uint _containerCapabilities:4;
// uint _unused:21;*/
typedef int(*sizeFunc)(const void *p);
typedef const void * (*atFunc)(const void *p, int);
typedef void (*moveIteratorFunc)(const void *p, void **);
enum Position { ToBegin, ToEnd };
typedef void (*moveIteratorFunc2)(const void *p, void **, Position position);
typedef void (*advanceFunc)(void **p, int);
typedef VariantData (*getFunc)( void * const *p, int metaTypeId, uint flags);
typedef void (*destroyIterFunc)(void **p);
typedef bool (*equalIterFunc)(void * const *p, void * const *other);
typedef void (*copyIterFunc)(void **, void * const *);
typedef void(*appendFunction)(const void *container, const void *newElement);
IteratorCapability iteratorCapabilities() {return static_cast<IteratorCapability>(_iteratorCapabilities & 0xF);}
uint revision() {return _iteratorCapabilities >> 4 & 0x7;}
uint containerCapabilities() {return _iteratorCapabilities >> 7 & 0xF;}
sizeFunc _size;
atFunc _at;
moveIteratorFunc _moveToBegin;
moveIteratorFunc _moveToEnd;
union {
moveIteratorFunc _moveToBegin;
moveIteratorFunc2 _moveTo;
};
union {
moveIteratorFunc _moveToEnd;
appendFunction _append;
};
advanceFunc _advance;
getFunc _get;
destroyIterFunc _destroyIter;
@ -1059,6 +1107,15 @@ public:
static void moveToEndImpl(const void *container, void **iterator)
{ IteratorOwner<typename T::const_iterator>::assign(iterator, static_cast<const T*>(container)->end()); }
template<class Container>
static void moveToImpl(const void *container, void **iterator, Position position)
{
if (position == ToBegin)
moveToBeginImpl<Container>(container, iterator);
else
moveToEndImpl<Container>(container, iterator);
}
template<class T>
static VariantData getImpl(void * const *iterator, int metaTypeId, uint flags)
{ return VariantData(metaTypeId, IteratorOwner<typename T::const_iterator>::getData(iterator), flags); }
@ -1069,11 +1126,11 @@ public:
, _iterator(nullptr)
, _metaType_id(qMetaTypeId<typename T::value_type>())
, _metaType_flags(QTypeInfo<typename T::value_type>::isPointer)
, _iteratorCapabilities(ContainerAPI<T>::IteratorCapabilities)
, _iteratorCapabilities(ContainerAPI<T>::IteratorCapabilities | (1 << 4) | (ContainerCapabilitiesImpl<T>::ContainerCapabilities << (4+3)))
, _size(sizeImpl<T>)
, _at(atImpl<T>)
, _moveToBegin(moveToBeginImpl<T>)
, _moveToEnd(moveToEndImpl<T>)
, _moveTo(moveToImpl<T>)
, _append(ContainerCapabilitiesImpl<T>::appendImpl)
, _advance(IteratorOwner<typename T::const_iterator>::advance)
, _get(getImpl<T>)
, _destroyIter(IteratorOwner<typename T::const_iterator>::destroy)
@ -1087,7 +1144,7 @@ public:
, _iterator(nullptr)
, _metaType_id(QMetaType::UnknownType)
, _metaType_flags(0)
, _iteratorCapabilities(0)
, _iteratorCapabilities(0 | (1 << 4) ) // no iterator capabilities, revision 1
, _size(nullptr)
, _at(nullptr)
, _moveToBegin(nullptr)
@ -1100,8 +1157,18 @@ public:
{
}
inline void moveToBegin() { _moveToBegin(_iterable, &_iterator); }
inline void moveToEnd() { _moveToEnd(_iterable, &_iterator); }
inline void moveToBegin() {
if (revision() == 0)
_moveToBegin(_iterable, &_iterator);
else
_moveTo(_iterable, &_iterator, ToBegin);
}
inline void moveToEnd() {
if (revision() == 0)
_moveToEnd(_iterable, &_iterator);
else
_moveTo(_iterable, &_iterator, ToEnd);
}
inline bool equal(const QSequentialIterableImpl&other) const { return _equalIter(&_iterator, &other._iterator); }
inline QSequentialIterableImpl &advance(int i) {
Q_ASSERT(i > 0 || _iteratorCapabilities & BiDirectionalCapability);
@ -1109,6 +1176,11 @@ public:
return *this;
}
inline void append(const void *newElement) {
if (containerCapabilities() & ContainerIsAppendable)
_append(_iterable, newElement);
}
inline VariantData getCurrent() const { return _get(&_iterator, _metaType_id, _metaType_flags); }
VariantData at(int idx) const

View File

@ -281,6 +281,8 @@ private slots:
void shouldDeleteVariantDataWorksForAssociative();
void fromStdVariant();
void qt4UuidDataStream();
void sequentialIterableEndianessSanityCheck();
void sequentialIterableAppend();
void preferDirectConversionOverInterfaces();
@ -5142,6 +5144,28 @@ void tst_QVariant::qt4UuidDataStream()
QCOMPARE(result.value<QUuid>(), source);
}
void tst_QVariant::sequentialIterableEndianessSanityCheck()
{
namespace QMTP = QtMetaTypePrivate;
uint oldIteratorCaps = QMTP::ForwardCapability | QMTP::BiDirectionalCapability | QMTP::RandomAccessCapability;
QMTP::QSequentialIterableImpl seqImpl {};
QCOMPARE(seqImpl.revision(), 1u);
memcpy(&seqImpl._iteratorCapabilities, &oldIteratorCaps, sizeof(oldIteratorCaps));
QCOMPARE(seqImpl.revision(), 0u);
}
void tst_QVariant::sequentialIterableAppend()
{
QVector<int> container {1, 2};
auto variant = QVariant::fromValue(container);
QVERIFY(variant.canConvert<QtMetaTypePrivate::QSequentialIterableImpl>());
auto asIterable = variant.value<QtMetaTypePrivate::QSequentialIterableImpl>();
const int i = 3, j = 4;
asIterable.append(&i);
asIterable.append(&j);
QCOMPARE(variant.value<QVector<int>>(), QVector<int> ({1, 2, 3, 4}));
}
void tst_QVariant::preferDirectConversionOverInterfaces()
{
using namespace QtMetaTypePrivate;