File indexing completed on 2024-04-28 15:39:56
0001 // SPDX-FileCopyrightText: 2003 - 2020 Jesper K. Pedersen <blackie@kde.org> 0002 // SPDX-FileCopyrightText: 2005, 2007 Dirk Mueller <mueller@kde.org> 0003 // SPDX-FileCopyrightText: 2008, 2010 Tuomas Suutari <tuomas@nepnep.net> 0004 // SPDX-FileCopyrightText: 2008 Henner Zeller <h.zeller@acm.org> 0005 // SPDX-FileCopyrightText: 2008 Jan Kundrát <jkt@flaska.net> 0006 // SPDX-FileCopyrightText: 2008 Laurent Montel <montel@kde.org> 0007 // SPDX-FileCopyrightText: 2020 Robert Krawitz <rlk@alum.mit.edu> 0008 // SPDX-FileCopyrightText: 2020 Tobias Leupold <tl@stonemx.de> 0009 // SPDX-FileCopyrightText: 2012 - 2023 Johannes Zarl-Zierl <johannes@zarl-zierl.at> 0010 0011 // SPDX-License-Identifier: GPL-2.0-or-later 0012 0013 #include "ImageDateCollection.h" 0014 #include "ImageInfoList.h" 0015 0016 DB::ImageDateCollection::ImageDateCollection(const ImageInfoList &list) 0017 { 0018 for (const auto &image : list) { 0019 const auto &date = image->date(); 0020 m_startIndex.insert(date.start(), date); 0021 } 0022 buildIndex(); 0023 } 0024 0025 void DB::ImageDateCollection::buildIndex() 0026 { 0027 StartIndexMap::ConstIterator startSearch = m_startIndex.constBegin(); 0028 Utilities::FastDateTime biggestEnd = QDate(1900, 1, 1).startOfDay(); 0029 for (StartIndexMap::ConstIterator it = m_startIndex.constBegin(); 0030 it != m_startIndex.constEnd(); 0031 ++it) { 0032 // We want a monotonic mapping end-date -> smallest-in-start-index. 0033 // Since we go through the start index sorted, lowest first, we just 0034 // have to keep the last pointer as long as we find smaller end-dates. 0035 // This should be rare as it only occurs if there are images that 0036 // actually represent a range not just a point in time. 0037 if (it.value().end() > biggestEnd) { 0038 biggestEnd = it.value().end(); 0039 startSearch = it; 0040 } 0041 m_endIndex.insert(it.value().end(), startSearch); 0042 } 0043 } 0044 0045 /** 0046 * @brief DB::ImageDateCollection::count 0047 * @param range 0048 * @return 0049 * @internal 0050 Previously, counting the elements was done by going through all elements 0051 and count the matches for a particular range, this unfortunately had 0052 O(n) complexity multiplied by m ranges we would get O(mn). 0053 0054 Henner Zeller rewrote it to its current state. The main idea now is to 0055 have all dates sorted so that it is possible to only look at the 0056 requested range. Since it is not points in time, we can't have just a 0057 simple sorted list. So we have two sorted maps, the m_startIndex and 0058 m_endIndex. m_startIndex is sorted by the start time of all ImageDates 0059 (which are in fact ranges) 0060 0061 If we would just look for Images that start _after_ the query-range, we 0062 would miscount, because there might be Image ranges starting before the 0063 query time but whose end time reaches into the query range this is what 0064 the m_endIndex is for: it is sortd by end-date; here we look for 0065 everything that is >= our query start. its value() part is basically a 0066 pointer to the position in the m_startIndex where we actually have to 0067 start looking. 0068 0069 The rest is simple: we determine the interesting start in 0070 m_startIndex using the m_endIndex and iterate through it until the 0071 elements in that sorted list have a start time that is larger than the 0072 query-end-range .. there will no more elements coming. 0073 0074 The above uses the fact that a QMap::constIterator iterates the map in 0075 sorted order. 0076 * @endinternal 0077 */ 0078 DB::ImageCount DB::ImageDateCollection::count(const DB::ImageDate &range) 0079 { 0080 if (m_cache.contains(range)) 0081 return m_cache[range]; 0082 0083 int exact = 0, rangeMatch = 0; 0084 0085 // We start searching in ranges that overlap our start search range, i.e. 0086 // where the end-date is higher than our search start. 0087 const EndIndexMap::ConstIterator endSearch = qAsConst(m_endIndex).lowerBound(range.start()); 0088 0089 if (endSearch != m_endIndex.constEnd()) { 0090 // qDebug() << "Counting images until" << endSearch.key() << "starting at" << endSearch.value().key(); 0091 for (StartIndexMap::ConstIterator it = endSearch.value(); 0092 it != m_startIndex.constEnd() && it.key() <= range.end(); 0093 ++it) { 0094 using MatchType = DB::ImageDate::MatchType; 0095 MatchType tp = it.value().isIncludedIn(range); 0096 switch (tp) { 0097 case MatchType::IsContained: 0098 exact++; 0099 break; 0100 case MatchType::Overlap: 0101 rangeMatch++; 0102 break; 0103 case MatchType::NoMatch: 0104 break; 0105 } 0106 } 0107 } 0108 0109 DB::ImageCount res(exact, rangeMatch); 0110 m_cache.insert(range, res); // TODO(hzeller) this might go now 0111 return res; 0112 } 0113 0114 Utilities::FastDateTime DB::ImageDateCollection::lowerLimit() const 0115 { 0116 if (!m_startIndex.empty()) { 0117 // skip null dates: 0118 for (StartIndexMap::ConstIterator it = m_startIndex.constBegin(); it != m_startIndex.constEnd(); ++it) { 0119 if (it.key().isValid()) 0120 return it.key(); 0121 } 0122 } 0123 // FIXME(jzarl): wouldn't it be better to return a null date instead? 0124 return QDate(1900, 1, 1).startOfDay(); 0125 } 0126 0127 Utilities::FastDateTime DB::ImageDateCollection::upperLimit() const 0128 { 0129 if (!m_endIndex.empty()) { 0130 EndIndexMap::ConstIterator highest = m_endIndex.constEnd(); 0131 --highest; 0132 return highest.key(); 0133 } 0134 // FIXME(jzarl): wouldn't it be better to return a null date instead? 0135 return QDate(2100, 1, 1).startOfDay(); 0136 } 0137 0138 // vi:expandtab:tabstop=4 shiftwidth=4: