Unify all mid() functions in QtBase.

Up to now, Qt had at least 3 different implementations of the mid().
Only QString::mid implementation was not crashing on edge cases and
was protected against overflows, therefore I picked that one as the
base implementation, even if it has weird semantics for an invalid
input.

As a side effect QVector::mid was slightly optimized to not detach in
all cases (which follows current QList behavior). Documentation of
QVector::mid and QList::mid was updated to not mention "copy of data"
which could suggest that the mid() result is detached.

QStringRef::mid was fixed and now it follows general Qt behavior, by
returning a null value for a null input.

Change-Id: Ie9ff5d98372bd193d66508e6dd92b6ed1180ad9b
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Jędrzej Nowacki 2014-02-14 16:32:31 +01:00 committed by The Qt Project
parent 8f3a393e41
commit 71fb3633e8
8 changed files with 114 additions and 60 deletions

View File

@ -130,4 +130,33 @@ void QArrayData::deallocate(QArrayData *data, size_t objectSize,
::free(data);
}
namespace QtPrivate {
/*!
\internal
*/
QContainerImplHelper::CutResult QContainerImplHelper::mid(int originalLength, int *_position, int *_length)
{
int &position = *_position;
int &length = *_length;
if (position > originalLength)
return Null;
if (position < 0) {
if (length < 0 || length + position >= originalLength)
return Full;
if (length + position <= 0)
return Null;
length += position;
position = 0;
} else if (uint(length) > uint(originalLength - position)) {
length = originalLength - position;
}
if (position == 0 && length == originalLength)
return Full;
return length > 0 ? Subset : Empty;
}
}
QT_END_NAMESPACE

View File

@ -361,6 +361,14 @@ namespace QtPrivate {
QT_PREPEND_NAMESPACE(QtPrivate::qMakeArrayLiteral)<Type>( Array )
#endif // !defined(Q_ARRAY_LITERAL)
namespace QtPrivate {
struct Q_CORE_EXPORT QContainerImplHelper
{
enum CutResult { Null, Empty, Full, Subset };
static CutResult mid(int originalLength, int *position, int *length);
};
}
QT_END_NAMESPACE
#endif // include guard

View File

@ -2677,19 +2677,22 @@ QByteArray QByteArray::right(int len) const
QByteArray QByteArray::mid(int pos, int len) const
{
if ((d->size == 0 && d->ref.isStatic()) || pos > d->size)
using namespace QtPrivate;
switch (QContainerImplHelper::mid(size(), &pos, &len)) {
case QContainerImplHelper::Null:
return QByteArray();
if (len < 0)
len = d->size - pos;
if (pos < 0) {
len += pos;
pos = 0;
case QContainerImplHelper::Empty:
{
QByteArrayDataPtr empty = { Data::allocate(0) };
return QByteArray(empty);
}
if (len + pos > d->size)
len = d->size - pos;
if (pos == 0 && len == d->size)
case QContainerImplHelper::Full:
return *this;
return QByteArray(d->data() + pos, len);
case QContainerImplHelper::Subset:
return QByteArray(d->data() + pos, len);
}
Q_UNREACHABLE();
return QByteArray();
}
/*!

View File

@ -491,11 +491,11 @@ void **QListData::erase(void **xi)
/*!
\fn QList<T> QList<T>::mid(int pos, int length) const
Returns a list whose elements are copied from this list,
Returns a sub-list which includes elements from this list,
starting at position \a pos. If \a length is -1 (the default), all
elements from \a pos are copied; otherwise \a length elements (or
elements from \a pos are included; otherwise \a length elements (or
all remaining elements if there are less than \a length elements)
are copied.
are included.
*/
/*! \fn QList::QList()

View File

@ -45,6 +45,7 @@
#include <QtCore/qalgorithms.h>
#include <QtCore/qiterator.h>
#include <QtCore/qrefcount.h>
#include <QtCore/qarraydata.h>
#include <iterator>
#include <list>
@ -646,10 +647,17 @@ inline void QList<T>::move(int from, int to)
template<typename T>
Q_OUTOFLINE_TEMPLATE QList<T> QList<T>::mid(int pos, int alength) const
{
if (alength < 0 || pos > size() - alength)
alength = size() - pos;
if (pos == 0 && alength == size())
using namespace QtPrivate;
switch (QContainerImplHelper::mid(size(), &pos, &alength)) {
case QContainerImplHelper::Null:
case QContainerImplHelper::Empty:
return QList<T>();
case QContainerImplHelper::Full:
return *this;
case QContainerImplHelper::Subset:
break;
}
QList<T> cpy;
if (alength <= 0)
return cpy;

View File

@ -4090,21 +4090,22 @@ QString QString::right(int n) const
QString QString::mid(int position, int n) const
{
if (position > d->size)
using namespace QtPrivate;
switch (QContainerImplHelper::mid(d->size, &position, &n)) {
case QContainerImplHelper::Null:
return QString();
if (position < 0) {
if (n < 0 || n + position >= d->size)
return *this;
if (n + position <= 0)
return QString();
n += position;
position = 0;
} else if (uint(n) > uint(d->size - position))
n = d->size - position;
if (position == 0 && n == d->size)
case QContainerImplHelper::Empty:
{
QStringDataPtr empty = { Data::allocate(0) };
return QString(empty);
}
case QContainerImplHelper::Full:
return *this;
return QString((const QChar*) d->data() + position, n);
case QContainerImplHelper::Subset:
return QString((const QChar*)d->data() + position, n);
}
Q_UNREACHABLE();
return QString();
}
/*!
@ -8794,19 +8795,19 @@ QStringRef QString::rightRef(int n) const
*/
QStringRef QStringRef::mid(int pos, int n) const
{
if (pos > m_size)
using namespace QtPrivate;
switch (QContainerImplHelper::mid(m_size, &pos, &n)) {
case QContainerImplHelper::Null:
return QStringRef();
if (pos < 0) {
if (n < 0 || n + pos >= m_size)
return QStringRef(m_string, m_position, m_size);
if (n + pos <= 0)
return QStringRef();
n += pos;
pos = 0;
} else if (uint(n) > uint(m_size - pos)) {
n = m_size - pos;
case QContainerImplHelper::Empty:
return QStringRef(m_string, 0, 0);
case QContainerImplHelper::Full:
return *this;
case QContainerImplHelper::Subset:
return QStringRef(m_string, pos + m_position, n);
}
return QStringRef(m_string, pos + m_position, n);
Q_UNREACHABLE();
return QStringRef();
}
/*!
@ -8831,19 +8832,19 @@ QStringRef QStringRef::mid(int pos, int n) const
*/
QStringRef QString::midRef(int position, int n) const
{
if (position > d->size)
using namespace QtPrivate;
switch (QContainerImplHelper::mid(d->size, &position, &n)) {
case QContainerImplHelper::Null:
return QStringRef();
if (position < 0) {
if (n < 0 || n + position >= d->size)
return QStringRef(this, 0, d->size);
if (n + position <= 0)
return QStringRef();
n += position;
position = 0;
} else if (uint(n) > uint(d->size - position))
n = d->size - position;
return QStringRef(this, position, n);
case QContainerImplHelper::Empty:
return QStringRef(this, 0, 0);
case QContainerImplHelper::Full:
return QStringRef(this, 0, d->size);
case QContainerImplHelper::Subset:
return QStringRef(this, position, n);
}
Q_UNREACHABLE();
return QStringRef();
}
/*!

View File

@ -180,11 +180,11 @@
/*!
\fn QVector<T> QVector::mid(int pos, int length = -1) const
Returns a vector whose elements are copied from this vector,
Returns a sub-vector which contains elements from this vector,
starting at position \a pos. If \a length is -1 (the default), all
elements after \a pos are copied; otherwise \a length elements (or
elements after \a pos are included; otherwise \a length elements (or
all remaining elements if there are less than \a length elements)
are copied.
are included.
*/

View File

@ -822,12 +822,17 @@ int QVector<T>::count(const T &t) const
template <typename T>
Q_OUTOFLINE_TEMPLATE QVector<T> QVector<T>::mid(int pos, int len) const
{
if (len < 0)
len = size() - pos;
if (pos == 0 && len == size())
using namespace QtPrivate;
switch (QContainerImplHelper::mid(d->size, &pos, &len)) {
case QContainerImplHelper::Null:
case QContainerImplHelper::Empty:
return QVector<T>();
case QContainerImplHelper::Full:
return *this;
if (pos + len > size())
len = size() - pos;
case QContainerImplHelper::Subset:
break;
}
QVector<T> copy;
copy.reserve(len);
for (int i = pos; i < pos + len; ++i)