File indexing completed on 2024-05-12 05:47:29

0001 /*
0002  * SPDX-FileCopyrightText: 2012 Peter Penz <peter.penz19@gmail.com>
0003  * SPDX-FileCopyrightText: 2012 Emmanuel Pescosta <emmanuelpescosta099@gmail.com>
0004  * SPDX-FileCopyrightText: 2013 Frank Reininghaus <frank78ac@googlemail.com>
0005  *
0006  * SPDX-License-Identifier: GPL-2.0-or-later
0007  */
0008 
0009 #ifndef KFILEITEMMODELSORTALGORITHM_H
0010 #define KFILEITEMMODELSORTALGORITHM_H
0011 
0012 #include <QtConcurrentRun>
0013 
0014 #include <algorithm>
0015 
0016 /**
0017  * Sorts the items using the merge sort algorithm is used to assure a
0018  * worst-case of O(n * log(n)) and to keep the number of comparisons low.
0019  *
0020  * The implementation is based on qStableSortHelper() from qalgorithms.h
0021  * SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
0022  */
0023 
0024 template<typename RandomAccessIterator, typename LessThan>
0025 static void mergeSort(RandomAccessIterator begin, RandomAccessIterator end, const LessThan &lessThan)
0026 {
0027     // The implementation is based on qStableSortHelper() from qalgorithms.h
0028     // SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
0029 
0030     const int span = end - begin;
0031     if (span < 2) {
0032         return;
0033     }
0034 
0035     const RandomAccessIterator middle = begin + span / 2;
0036     mergeSort(begin, middle, lessThan);
0037     mergeSort(middle, end, lessThan);
0038     merge(begin, middle, end, lessThan);
0039 }
0040 
0041 /**
0042  * Uses up to \a numberOfThreads threads to sort the items between
0043  * \a begin and \a end. Only item ranges longer than
0044  * \a parallelMergeSortingThreshold are split to be sorted by two different
0045  * threads.
0046  *
0047  * The comparison function \a lessThan must be reentrant.
0048  */
0049 
0050 template<typename RandomAccessIterator, typename LessThan>
0051 static void
0052 parallelMergeSort(RandomAccessIterator begin, RandomAccessIterator end, LessThan lessThan, int numberOfThreads, int parallelMergeSortingThreshold = 100)
0053 {
0054     const int span = end - begin;
0055 
0056     if (numberOfThreads > 1 && span > parallelMergeSortingThreshold) {
0057         const int newNumberOfThreads = numberOfThreads / 2;
0058         const RandomAccessIterator middle = begin + span / 2;
0059 
0060         QFuture<void> future =
0061             QtConcurrent::run(parallelMergeSort<RandomAccessIterator, LessThan>, begin, middle, lessThan, newNumberOfThreads, parallelMergeSortingThreshold);
0062         parallelMergeSort(middle, end, lessThan, newNumberOfThreads, parallelMergeSortingThreshold);
0063 
0064         future.waitForFinished();
0065 
0066         merge(begin, middle, end, lessThan);
0067     } else {
0068         mergeSort(begin, end, lessThan);
0069     }
0070 }
0071 
0072 /**
0073  * Merges the sorted item ranges between \a begin and \a pivot and
0074  * between \a pivot and \a end into a single sorted range between
0075  * \a begin and \a end.
0076  *
0077  * The implementation is based on qMerge() from qalgorithms.h
0078  * SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
0079  */
0080 
0081 template<typename RandomAccessIterator, typename LessThan>
0082 static void merge(RandomAccessIterator begin, RandomAccessIterator pivot, RandomAccessIterator end, const LessThan &lessThan)
0083 {
0084     // The implementation is based on qMerge() from qalgorithms.h
0085     // SPDX-FileCopyrightText: 2011 Nokia Corporation and/or its subsidiary(-ies).
0086 
0087     const int len1 = pivot - begin;
0088     const int len2 = end - pivot;
0089 
0090     if (len1 == 0 || len2 == 0) {
0091         return;
0092     }
0093 
0094     if (len1 + len2 == 2) {
0095         if (lessThan(*(begin + 1), *(begin))) {
0096             std::swap(*begin, *(begin + 1));
0097         }
0098         return;
0099     }
0100 
0101     RandomAccessIterator firstCut;
0102     RandomAccessIterator secondCut;
0103     int len2Half;
0104     if (len1 > len2) {
0105         const int len1Half = len1 / 2;
0106         firstCut = begin + len1Half;
0107         secondCut = std::lower_bound<RandomAccessIterator, decltype(*firstCut), const LessThan &>(pivot, end, *firstCut, lessThan);
0108         len2Half = secondCut - pivot;
0109     } else {
0110         len2Half = len2 / 2;
0111         secondCut = pivot + len2Half;
0112         firstCut = std::upper_bound<RandomAccessIterator, decltype(*secondCut), const LessThan &>(begin, pivot, *secondCut, lessThan);
0113     }
0114 
0115     std::rotate(firstCut, pivot, secondCut);
0116 
0117     RandomAccessIterator newPivot = firstCut + len2Half;
0118     merge(begin, firstCut, newPivot, lessThan);
0119     merge(newPivot, secondCut, end, lessThan);
0120 }
0121 
0122 #endif