File indexing completed on 2024-09-15 04:36:25

0001 /*
0002     SPDX-FileCopyrightText: 2007 Volker Krause <vkrause@kde.org>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "datastream_p_p.h"
0008 #include "imapset_p.h"
0009 
0010 #include <QSharedData>
0011 
0012 #include <limits>
0013 
0014 namespace Akonadi
0015 {
0016 class ImapIntervalPrivate : public QSharedData
0017 {
0018 public:
0019     ImapInterval::Id begin = 0;
0020     ImapInterval::Id end = 0;
0021 };
0022 
0023 class ImapSetPrivate : public QSharedData
0024 {
0025 public:
0026     template<typename T>
0027     void add(const T &values)
0028     {
0029         T vals = values;
0030         std::sort(vals.begin(), vals.end());
0031         for (int i = 0; i < vals.count(); ++i) {
0032             const int begin = vals[i];
0033             Q_ASSERT(begin >= 0);
0034             if (i == vals.count() - 1) {
0035                 intervals << ImapInterval(begin, begin);
0036                 break;
0037             }
0038             do {
0039                 ++i;
0040                 Q_ASSERT(vals[i] >= 0);
0041                 if (vals[i] != (vals[i - 1] + 1)) {
0042                     --i;
0043                     break;
0044                 }
0045             } while (i < vals.count() - 1);
0046             intervals << ImapInterval(begin, vals[i]);
0047         }
0048     }
0049 
0050     ImapInterval::List intervals;
0051 };
0052 
0053 ImapInterval::ImapInterval()
0054     : d(new ImapIntervalPrivate)
0055 {
0056 }
0057 
0058 ImapInterval::ImapInterval(const ImapInterval &other)
0059     : d(other.d)
0060 {
0061 }
0062 
0063 ImapInterval::ImapInterval(Id begin, Id end)
0064     : d(new ImapIntervalPrivate)
0065 {
0066     d->begin = begin;
0067     d->end = end;
0068 }
0069 
0070 ImapInterval::~ImapInterval() = default;
0071 
0072 ImapInterval &ImapInterval::operator=(const ImapInterval &other)
0073 {
0074     if (this != &other) {
0075         d = other.d;
0076     }
0077 
0078     return *this;
0079 }
0080 
0081 bool ImapInterval::operator==(const ImapInterval &other) const
0082 {
0083     return (d->begin == other.d->begin && d->end == other.d->end);
0084 }
0085 
0086 ImapInterval::Id ImapInterval::size() const
0087 {
0088     if (!d->begin && !d->end) {
0089         return 0;
0090     }
0091 
0092     return (d->end - d->begin + 1);
0093 }
0094 
0095 bool ImapInterval::hasDefinedBegin() const
0096 {
0097     return (d->begin != 0);
0098 }
0099 
0100 ImapInterval::Id ImapInterval::begin() const
0101 {
0102     return d->begin;
0103 }
0104 
0105 bool ImapInterval::hasDefinedEnd() const
0106 {
0107     return (d->end != 0);
0108 }
0109 
0110 ImapInterval::Id ImapInterval::end() const
0111 {
0112     if (hasDefinedEnd()) {
0113         return d->end;
0114     }
0115 
0116     return std::numeric_limits<Id>::max();
0117 }
0118 
0119 void ImapInterval::setBegin(Id value)
0120 {
0121     Q_ASSERT(value >= 0);
0122     Q_ASSERT(value <= d->end || !hasDefinedEnd());
0123     d->begin = value;
0124 }
0125 
0126 void ImapInterval::setEnd(Id value)
0127 {
0128     Q_ASSERT(value >= 0);
0129     Q_ASSERT(value >= d->begin || !hasDefinedBegin());
0130     d->end = value;
0131 }
0132 
0133 QByteArray Akonadi::ImapInterval::toImapSequence() const
0134 {
0135     if (size() == 0) {
0136         return QByteArray();
0137     }
0138 
0139     if (size() == 1) {
0140         return QByteArray::number(d->begin);
0141     }
0142 
0143     QByteArray rv = QByteArray::number(d->begin) + ':';
0144 
0145     if (hasDefinedEnd()) {
0146         rv += QByteArray::number(d->end);
0147     } else {
0148         rv += '*';
0149     }
0150 
0151     return rv;
0152 }
0153 
0154 ImapSet::ImapSet()
0155     : d(new ImapSetPrivate)
0156 {
0157 }
0158 
0159 ImapSet::ImapSet(Id id)
0160     : d(new ImapSetPrivate)
0161 {
0162     add(QList<Id>() << id);
0163 }
0164 ImapSet::ImapSet(const QList<qint64> &ids)
0165     : d(new ImapSetPrivate)
0166 {
0167     add(ids);
0168 }
0169 
0170 ImapSet::ImapSet(const ImapInterval &interval)
0171     : d(new ImapSetPrivate)
0172 {
0173     add(interval);
0174 }
0175 
0176 ImapSet::ImapSet(const ImapSet &other)
0177     : d(other.d)
0178 {
0179 }
0180 
0181 ImapSet::~ImapSet()
0182 {
0183 }
0184 
0185 ImapSet ImapSet::all()
0186 {
0187     ImapSet set;
0188     set.add(ImapInterval(1, 0));
0189     return set;
0190 }
0191 
0192 ImapSet &ImapSet::operator=(const ImapSet &other)
0193 {
0194     if (this != &other) {
0195         d = other.d;
0196     }
0197 
0198     return *this;
0199 }
0200 
0201 bool ImapSet::operator==(const ImapSet &other) const
0202 {
0203     return d->intervals == other.d->intervals;
0204 }
0205 
0206 void ImapSet::add(const QList<Id> &values)
0207 {
0208     d->add(values);
0209 }
0210 
0211 void ImapSet::add(const QSet<Id> &values)
0212 {
0213     QList<Id> v;
0214     v.reserve(values.size());
0215     for (QSet<Id>::ConstIterator iter = values.constBegin(); iter != values.constEnd(); ++iter) {
0216         v.push_back(*iter);
0217     }
0218 
0219     add(v);
0220 }
0221 
0222 void ImapSet::add(const ImapInterval &interval)
0223 {
0224     d->intervals << interval;
0225 }
0226 
0227 QByteArray ImapSet::toImapSequenceSet() const
0228 {
0229     QByteArray rv;
0230     for (auto iter = d->intervals.cbegin(), end = d->intervals.cend(); iter != end; ++iter) {
0231         if (iter != d->intervals.cbegin()) {
0232             rv += ',';
0233         }
0234         rv += iter->toImapSequence();
0235     }
0236 
0237     return rv;
0238 }
0239 
0240 ImapInterval::List ImapSet::intervals() const
0241 {
0242     return d->intervals;
0243 }
0244 
0245 bool ImapSet::isEmpty() const
0246 {
0247     return d->intervals.isEmpty() || (d->intervals.size() == 1 && d->intervals.at(0).size() == 0);
0248 }
0249 
0250 void ImapSet::optimize()
0251 {
0252     // There's nothing to optimize if we have fewer than 2 intervals
0253     if (d->intervals.size() < 2) {
0254         return;
0255     }
0256 
0257     // Sort the intervals in ascending order by their beginning value
0258     std::sort(d->intervals.begin(), d->intervals.end(), [](const ImapInterval &lhs, const ImapInterval &rhs) {
0259         return lhs.begin() < rhs.begin();
0260     });
0261 
0262     auto it = d->intervals.begin();
0263     while (it != d->intervals.end() && it != std::prev(d->intervals.end())) {
0264         auto next = std::next(it);
0265         // +1 so that we also merge neighbouring intervals, e.g. 1:2,3:4 -> 1:4
0266         if (it->hasDefinedEnd() && it->end() + 1 >= next->begin()) {
0267             next->setBegin(it->begin());
0268             if (next->hasDefinedEnd() && it->end() > next->end()) {
0269                 next->setEnd(it->end());
0270             }
0271             it = d->intervals.erase(it);
0272         } else if (!it->hasDefinedEnd()) {
0273             // We can eat up all the remaining intervals
0274             it = d->intervals.erase(next, d->intervals.end());
0275         } else {
0276             ++it;
0277         }
0278     }
0279 }
0280 
0281 Protocol::DataStream &operator<<(Protocol::DataStream &stream, const Akonadi::ImapInterval &interval)
0282 {
0283     return stream << interval.d->begin << interval.d->end;
0284 }
0285 
0286 Protocol::DataStream &operator>>(Protocol::DataStream &stream, Akonadi::ImapInterval &interval)
0287 {
0288     return stream >> interval.d->begin >> interval.d->end;
0289 }
0290 
0291 Protocol::DataStream &operator<<(Protocol::DataStream &stream, const Akonadi::ImapSet &set)
0292 {
0293     return stream << set.d->intervals;
0294 }
0295 
0296 Protocol::DataStream &operator>>(Protocol::DataStream &stream, Akonadi::ImapSet &set)
0297 {
0298     return stream >> set.d->intervals;
0299 }
0300 
0301 } // namespace Akonadi
0302 
0303 using namespace Akonadi;
0304 
0305 QDebug operator<<(QDebug d, const Akonadi::ImapInterval &interval)
0306 {
0307     d << interval.toImapSequence();
0308     return d;
0309 }
0310 
0311 QDebug operator<<(QDebug d, const Akonadi::ImapSet &set)
0312 {
0313     d << set.toImapSequenceSet();
0314     return d;
0315 }