File indexing completed on 2024-05-12 05:17:14

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