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

0001 /*
0002     Copyright (c) 2007 Volker Krause <vkrause@kde.org>
0003 
0004     This library is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Library General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or (at your
0007     option) any later version.
0008 
0009     This library is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU Library General Public License
0015     along with this library; see the file COPYING.LIB.  If not, write to the
0016     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
0017     02110-1301, USA.
0018 */
0019 
0020 #include "imapset.h"
0021 
0022 #include <QtCore/QSharedData>
0023 
0024 using namespace KIMAP2;
0025 
0026 class ImapInterval::Private : public QSharedData
0027 {
0028 public:
0029     Private() :
0030         QSharedData(),
0031         begin(0),
0032         end(0)
0033     {}
0034 
0035     Private(const Private &other) :
0036         QSharedData(other)
0037     {
0038         begin = other.begin;
0039         end = other.end;
0040     }
0041 
0042     Id begin;
0043     Id end;
0044 };
0045 
0046 class ImapSet::Private : public QSharedData
0047 {
0048 public:
0049     Private() : QSharedData() {}
0050     Private(const Private &other) :
0051         QSharedData(other)
0052     {
0053         intervals = other.intervals;
0054     }
0055 
0056     ImapInterval::List intervals;
0057 };
0058 
0059 ImapInterval::ImapInterval() :
0060     d(new Private)
0061 {
0062 }
0063 
0064 ImapInterval::ImapInterval(const ImapInterval &other) :
0065     d(other.d)
0066 {
0067 }
0068 
0069 ImapInterval::ImapInterval(Id begin, Id end) :
0070     d(new Private)
0071 {
0072     d->begin = begin;
0073     d->end = end;
0074 }
0075 
0076 ImapInterval::~ ImapInterval()
0077 {
0078 }
0079 
0080 ImapInterval &ImapInterval::operator =(const ImapInterval &other)
0081 {
0082     if (this != &other) {
0083         d = other.d;
0084     }
0085     return *this;
0086 }
0087 
0088 bool ImapInterval::operator ==(const ImapInterval &other) const
0089 {
0090     return (d->begin == other.d->begin && d->end == other.d->end);
0091 }
0092 
0093 ImapInterval::Id ImapInterval::size() const
0094 {
0095     if (!d->begin && !d->end) {
0096         return 0;
0097     }
0098     if (d->begin && !d->end) {
0099         return Q_INT64_C(0x7FFFFFFFFFFFFFFF) - d->begin + 1;
0100     }
0101     return d->end - d->begin + 1;
0102 }
0103 
0104 bool ImapInterval::hasDefinedBegin() const
0105 {
0106     return d->begin != 0;
0107 }
0108 
0109 ImapInterval::Id ImapInterval::begin() const
0110 {
0111     return d->begin;
0112 }
0113 
0114 bool ImapInterval::hasDefinedEnd() const
0115 {
0116     return d->end != 0;
0117 }
0118 
0119 ImapInterval::Id ImapInterval::end() const
0120 {
0121     if (hasDefinedEnd()) {
0122         return d->end;
0123     }
0124     return 0xFFFFFFFF; // should be INT_MAX, but where is that defined again?
0125 }
0126 
0127 void ImapInterval::setBegin(Id value)
0128 {
0129     Q_ASSERT(value >= 0);
0130     Q_ASSERT(value <= d->end || !hasDefinedEnd());
0131     d->begin = value;
0132 }
0133 
0134 void ImapInterval::setEnd(Id value)
0135 {
0136     Q_ASSERT(value >= 0);
0137     Q_ASSERT(value >= d->begin || !hasDefinedBegin());
0138     d->end = value;
0139 }
0140 
0141 QByteArray ImapInterval::toImapSequence() const
0142 {
0143     if (size() == 0) {
0144         return QByteArray();
0145     }
0146     if (size() == 1) {
0147         return QByteArray::number(d->begin);
0148     }
0149     QByteArray rv;
0150     rv += QByteArray::number(d->begin) + ':';
0151     if (hasDefinedEnd()) {
0152         rv += QByteArray::number(d->end);
0153     } else {
0154         rv += '*';
0155     }
0156     return rv;
0157 }
0158 
0159 ImapInterval ImapInterval::fromImapSequence(const QByteArray &sequence)
0160 {
0161     QList<QByteArray> values = sequence.split(':');
0162     if (values.isEmpty() || values.size() > 2) {
0163         return ImapInterval();
0164     }
0165 
0166     bool ok = false;
0167     Id begin = values[0].toLongLong(&ok);
0168 
0169     if (!ok) {
0170         return ImapInterval();
0171     }
0172 
0173     Id end;
0174 
0175     if (values.size() == 1) {
0176         end = begin;
0177     } else if (values[1] == QByteArray("*")) {
0178         end = 0;
0179     } else {
0180         ok = false;
0181         end = values[1].toLongLong(&ok);
0182         if (!ok) {
0183             return ImapInterval();
0184         }
0185     }
0186 
0187     return ImapInterval(begin, end);
0188 }
0189 
0190 ImapSet::ImapSet() :
0191     d(new Private)
0192 {
0193 }
0194 
0195 ImapSet::ImapSet(Id begin, Id end) :
0196     d(new Private)
0197 {
0198     add(ImapInterval(begin, end));
0199 }
0200 
0201 ImapSet::ImapSet(Id value) :
0202     d(new Private)
0203 {
0204     add(QVector<Id>() << value);
0205 }
0206 
0207 ImapSet::ImapSet(const ImapSet &other) :
0208     d(other.d)
0209 {
0210 }
0211 
0212 ImapSet::~ImapSet()
0213 {
0214 }
0215 
0216 ImapSet &ImapSet::operator =(const ImapSet &other)
0217 {
0218     if (this != &other) {
0219         d = other.d;
0220     }
0221     return *this;
0222 }
0223 
0224 bool ImapSet::operator ==(const ImapSet &other) const
0225 {
0226     if (d->intervals.size() != other.d->intervals.size()) {
0227         return false;
0228     }
0229 
0230     foreach (const ImapInterval &interval, d->intervals) {
0231         if (!other.d->intervals.contains(interval)) {
0232             return false;
0233         }
0234     }
0235 
0236     return true;
0237 }
0238 
0239 void ImapSet::add(Id value)
0240 {
0241     add(QVector<Id>() << value);
0242 }
0243 
0244 void ImapSet::add(const QVector<Id> &values)
0245 {
0246     QVector<Id> vals = values;
0247     std::sort(vals.begin(), vals.end());
0248     for (auto i = 0; i < vals.count(); ++i) {
0249         const auto begin = vals[i];
0250         Q_ASSERT(begin >= 0);
0251         if (i == vals.count() - 1) {
0252             d->intervals << ImapInterval(begin, begin);
0253             break;
0254         }
0255         do {
0256             ++i;
0257             Q_ASSERT(vals[i] >= 0);
0258             if (vals[i] != (vals[i - 1] + 1)) {
0259                 --i;
0260                 break;
0261             }
0262         } while (i < vals.count() - 1);
0263         d->intervals << ImapInterval(begin, vals[i]);
0264     }
0265 }
0266 
0267 void ImapSet::add(const ImapInterval &interval)
0268 {
0269     d->intervals << interval;
0270 }
0271 
0272 QByteArray ImapSet::toImapSequenceSet() const
0273 {
0274     QList<QByteArray> rv;
0275     rv.reserve(d->intervals.count());
0276     foreach (const ImapInterval &interval, d->intervals) {
0277         rv << interval.toImapSequence();
0278     }
0279 
0280     QByteArray result;
0281 
0282     if (!rv.isEmpty()) {
0283         result = rv.first();
0284         QList<QByteArray>::ConstIterator it = rv.constBegin();
0285         ++it;
0286         for (; it != rv.constEnd(); ++it) {
0287             result += ',' + (*it);
0288         }
0289     }
0290 
0291     return result;
0292 }
0293 
0294 ImapSet ImapSet::fromImapSequenceSet(const QByteArray &sequence)
0295 {
0296     ImapSet result;
0297 
0298     QList<QByteArray> intervals = sequence.split(',');
0299 
0300     foreach (const QByteArray &interval, intervals) {
0301         if (!interval.isEmpty()) {
0302             result.add(ImapInterval::fromImapSequence(interval));
0303         }
0304     }
0305 
0306     return result;
0307 }
0308 
0309 ImapInterval::List ImapSet::intervals() const
0310 {
0311     return d->intervals;
0312 }
0313 
0314 bool ImapSet::isEmpty() const
0315 {
0316     return d->intervals.isEmpty();
0317 }
0318 
0319 void ImapSet::optimize()
0320 {
0321     // There's nothing to optimize if we have fewer than 2 intervals
0322     if (d->intervals.size() < 2) {
0323         return;
0324     }
0325 
0326     // Sort the intervals in ascending order by their beginning value
0327     std::sort(d->intervals.begin(), d->intervals.end(),
0328               [](const ImapInterval &lhs, const ImapInterval &rhs) {
0329                    return lhs.begin() < rhs.begin();
0330               });
0331 
0332     auto it = d->intervals.begin();
0333     while (it != d->intervals.end() && it != std::prev(d->intervals.end())) {
0334         auto next = std::next(it);
0335         // +1 so that we also merge neighbouring intervals, e.g. 1:2,3:4 -> 1:4
0336         if (it->hasDefinedEnd() && it->end() + 1 >= next->begin()) {
0337             next->setBegin(it->begin());
0338             if (next->hasDefinedEnd() && it->end() > next->end()) {
0339                 next->setEnd(it->end());
0340             }
0341             it = d->intervals.erase(it);
0342         } else if (!it->hasDefinedEnd()) {
0343             // We can eat up all the remaining intervals
0344             it = d->intervals.erase(next, d->intervals.end());
0345         } else {
0346             ++it;
0347         }
0348     }
0349 }
0350 
0351 QDebug &operator<<(QDebug &d, const ImapInterval &interval)
0352 {
0353     d << interval.toImapSequence();
0354     return d;
0355 }
0356 
0357 QDebug &operator<<(QDebug &d, const ImapSet &set)
0358 {
0359     d << set.toImapSequenceSet();
0360     return d;
0361 }