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 }