@@ -16,6 +16,13 @@ in the source distribution for its full text.
1616#include "XUtils.h"
1717
1818
19+ typedef int (* CompareWithContext )(const void * , const void * , void * );
20+
21+ typedef struct VectorSortContext_ {
22+ Object_Compare compare ;
23+ void * compareContext ;
24+ } VectorSortContext ;
25+
1926Vector * Vector_new (const ObjectClass * type , bool owner , int size ) {
2027 Vector * this ;
2128
@@ -93,91 +100,159 @@ void Vector_prune(Vector* this) {
93100 memset (this -> array , '\0' , this -> arraySize * sizeof (Object * ));
94101}
95102
96- //static int comparisons = 0;
97-
98- static void swap (Object * * array , int indexA , int indexB ) {
99- assert (indexA >= 0 );
100- assert (indexB >= 0 );
101- Object * tmp = array [indexA ];
102- array [indexA ] = array [indexB ];
103- array [indexB ] = tmp ;
103+ ATTR_NONNULL
104+ static void swapByte (char * p1 , char * p2 ) {
105+ char temp = * p1 ;
106+ * p1 = * p2 ;
107+ * p2 = temp ;
104108}
105109
106- static int partition (Object * * array , int left , int right , int pivotIndex , Object_Compare compare ) {
107- const Object * pivotValue = array [pivotIndex ];
108- swap (array , pivotIndex , right );
109- int storeIndex = left ;
110- for (int i = left ; i < right ; i ++ ) {
111- //comparisons++;
112- if (compare (array [i ], pivotValue ) <= 0 ) {
113- swap (array , i , storeIndex );
114- storeIndex ++ ;
110+ ATTR_NONNULL
111+ static void rotate (void * buffer , size_t leftSize , size_t rightSize ) {
112+ if (rightSize == 0 )
113+ return ;
114+
115+ char * p1 = buffer ;
116+ char * p2 = p1 + leftSize ;
117+ char * mid = p2 ;
118+ const char * const end = mid + rightSize ;
119+
120+ while (true) {
121+ assert (p1 <= mid );
122+ assert (mid <= p2 );
123+ assert (p2 <= end );
124+ if (p2 >= end ) {
125+ assert (mid < end );
126+ p2 = mid ;
115127 }
128+
129+ if (p1 >= p2 )
130+ break ;
131+
132+ if (p1 >= mid )
133+ mid = p2 ;
134+
135+ swapByte (p1 , p2 );
136+ p1 += 1 ;
137+ p2 += 1 ;
116138 }
117- swap (array , storeIndex , right );
118- return storeIndex ;
119139}
120140
121- static void quickSort (Object * * array , int left , int right , Object_Compare compare ) {
122- if (left >= right )
141+ ATTR_NONNULL_N (1 , 5 )
142+ static void mergeRuns (void * array , size_t leftLen , size_t rightLen , size_t size , CompareWithContext compare , void * context ) {
143+ assert (size > 0 );
144+ if (leftLen == 0 || rightLen == 0 || size == 0 )
123145 return ;
124146
125- int pivotIndex = left + (right - left ) / 2 ;
126- int pivotNewIndex = partition (array , left , right , pivotIndex , compare );
127- quickSort (array , left , pivotNewIndex - 1 , compare );
128- quickSort (array , pivotNewIndex + 1 , right , compare );
129- }
147+ char * p1 = array ;
148+ char * p2 = p1 + leftLen * size ;
149+ char * mid = p2 ;
150+ const char * const end = mid + rightLen * size ;
130151
131- // If I were to use only one sorting algorithm for both cases, it would probably be this one:
132- /*
152+ for (size_t limit = (leftLen + rightLen ) / 2 ; limit > 0 ; limit -- ) {
153+ assert (p1 <= mid );
154+ assert (mid <= p2 );
155+ assert (p2 <= end );
133156
134- static void combSort(Object** array, int left, int right, Object_Compare compare) {
135- int gap = right - left;
136- bool swapped = true;
137- while ((gap > 1) || swapped) {
138- if (gap > 1) {
139- gap = (int)((double)gap / 1.247330950103979);
140- }
141- swapped = false;
142- for (int i = left; gap + i <= right; i++) {
143- comparisons++;
144- if (compare(array[i], array[i+gap]) > 0) {
145- swap(array, i, i+gap);
146- swapped = true;
147- }
157+ if (p1 >= mid || p2 >= end )
158+ break ;
159+
160+ if (compare (p1 , p2 , context ) <= 0 ) {
161+ p1 += size ;
162+ } else {
163+ p2 += size ;
148164 }
149165 }
166+
167+ rotate (p1 , (size_t )(mid - p1 ), (size_t )(p2 - mid ));
168+
169+ leftLen = (size_t )(p1 - (char * )array ) / size ;
170+ rightLen = (size_t )(p2 - mid ) / size ;
171+ mergeRuns (array , leftLen , rightLen , size , compare , context );
172+
173+ leftLen = (size_t )(mid - p1 ) / size ;
174+ rightLen = (size_t )(end - p2 ) / size ;
175+ mergeRuns (p1 + (p2 - mid ), leftLen , rightLen , size , compare , context );
150176}
151177
152- */
178+ ATTR_NONNULL_N (1 , 5 )
179+ static size_t mergeSortSubarray (void * array , size_t unsortedLen , size_t limit , size_t size , CompareWithContext compare , void * context ) {
180+ assert (size > 0 );
181+ if (size == 0 )
182+ return 0 ;
183+
184+ // The initial level of this function call must set "limit" to 0. Subsequent
185+ // levels of recursion will have "limit" no less than the previous level.
186+
187+ // A run is a sorted subarray. Each recursive call of this function keeps
188+ // the lengths of two runs. At most O(log(n)) lengths of runs will be
189+ // tracked on the call stack.
190+ size_t runLen [3 ] = {0 };
191+ while (unsortedLen > 0 ) {
192+ size_t totalLen = unsortedLen ;
193+ while (true) {
194+ if (-- unsortedLen < limit )
195+ return 0 ;
196+
197+ if (unsortedLen == 0 )
198+ break ;
153199
154- static void insertionSort (Object * * array , int left , int right , Object_Compare compare ) {
155- for (int i = left + 1 ; i <= right ; i ++ ) {
156- Object * t = array [i ];
157- int j = i - 1 ;
158- while (j >= left ) {
159- //comparisons++;
160- if (compare (array [j ], t ) <= 0 )
200+ const char * p2 = (const char * )array + unsortedLen * size ;
201+ if (compare (p2 - 1 * size , p2 , context ) > 0 ) {
161202 break ;
203+ }
204+ }
205+ runLen [1 ] = totalLen - unsortedLen ;
206+
207+ bool reachesLimit = false;
208+
209+ assert (runLen [2 ] > 0 || runLen [0 ] == 0 );
210+ if (runLen [2 ] > 0 ) {
211+ size_t nextLimit = limit ;
212+ if (unsortedLen > runLen [2 ] + limit ) {
213+ nextLimit = unsortedLen - runLen [2 ];
214+ } else {
215+ reachesLimit = true;
216+ }
217+
218+ runLen [0 ] = mergeSortSubarray (array , unsortedLen , nextLimit , size , compare , context );
219+ unsortedLen -= runLen [0 ];
220+
221+ char * p1 = (char * )array + unsortedLen * size ;
222+ mergeRuns (p1 , runLen [0 ], runLen [1 ], size , compare , context );
223+ runLen [1 ] += runLen [0 ];
224+ runLen [0 ] = 0 ;
162225
163- array [j + 1 ] = array [j ];
164- j -- ;
226+ mergeRuns (p1 , runLen [1 ], runLen [2 ], size , compare , context );
227+ }
228+ runLen [2 ] += runLen [1 ];
229+ runLen [1 ] = 0 ;
230+
231+ if (reachesLimit ) {
232+ break ;
165233 }
166- array [j + 1 ] = t ;
167234 }
235+ return runLen [2 ];
168236}
169237
170- void Vector_quickSortCustomCompare ( Vector * this , Object_Compare compare ) {
171- assert ( compare );
172- assert ( Vector_isConsistent ( this )) ;
173- quickSort ( this -> array , 0 , this -> items - 1 , compare );
174- assert ( Vector_isConsistent ( this ) );
238+ ATTR_NONNULL
239+ static int Vector_sortCompare ( const void * p1 , const void * p2 , void * context ) {
240+ VectorSortContext * vc = ( VectorSortContext * ) context ;
241+
242+ return vc -> compare ( * ( const void * const * ) p1 , * ( const void * const * ) p2 );
175243}
176244
177- void Vector_insertionSort (Vector * this ) {
178- assert (this -> type -> compare );
245+ ATTR_NONNULL_N (1 )
246+ void Vector_sort (Vector * this , Object_Compare compare , void * context ) {
247+ VectorSortContext vc = {
248+ .compare = compare ? compare : this -> type -> compare ,
249+ .compareContext = context ,
250+ };
251+ assert (vc .compare );
179252 assert (Vector_isConsistent (this ));
180- insertionSort (this -> array , 0 , this -> items - 1 , this -> type -> compare );
253+
254+ (void )mergeSortSubarray (this -> array , this -> items , 0 , sizeof (* this -> array ), Vector_sortCompare , & vc );
255+
181256 assert (Vector_isConsistent (this ));
182257}
183258
0 commit comments