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 }