@@ -10,12 +10,20 @@ in the source distribution for its full text.
1010#include "Vector.h"
1111
1212#include <assert.h>
13+ #include <stdint.h>
1314#include <stdlib.h>
1415#include <string.h>
1516
1617#include "XUtils.h"
1718
1819
20+ typedef int (* CompareWithContext )(const void * , const void * , void * );
21+
22+ typedef struct VectorSortContext_ {
23+ Object_Compare compare ;
24+ void * compareContext ;
25+ } VectorSortContext ;
26+
1927Vector * Vector_new (const ObjectClass * type , bool owner , int size ) {
2028 Vector * this ;
2129
@@ -93,91 +101,168 @@ void Vector_prune(Vector* this) {
93101 memset (this -> array , '\0' , this -> arraySize * sizeof (Object * ));
94102}
95103
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 ;
104+ ATTR_NONNULL
105+ static void swapByte (char * p1 , char * p2 ) {
106+ char temp = * p1 ;
107+ * p1 = * p2 ;
108+ * p2 = temp ;
104109}
105110
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 ++ ;
111+ ATTR_NONNULL
112+ static void rotate (void * buffer , size_t leftSize , size_t rightSize ) {
113+ if (rightSize == 0 )
114+ return ;
115+
116+ char * p1 = buffer ;
117+ char * p2 = p1 + leftSize ;
118+ char * mid = p2 ;
119+ const char * const end = mid + rightSize ;
120+
121+ while (true) {
122+ // Ensure there is no arithmetic overflow on input.
123+ assert (p1 <= mid );
124+ assert (mid <= p2 );
125+ assert (p2 <= end );
126+
127+ if (p2 >= end ) {
128+ assert (mid < end );
129+ p2 = mid ;
115130 }
131+
132+ if (p1 >= p2 )
133+ break ;
134+
135+ if (p1 >= mid )
136+ mid = p2 ;
137+
138+ swapByte (p1 , p2 );
139+ p1 += 1 ;
140+ p2 += 1 ;
116141 }
117- swap (array , storeIndex , right );
118- return storeIndex ;
119142}
120143
121- static void quickSort (Object * * array , int left , int right , Object_Compare compare ) {
122- if (left >= right )
144+ ATTR_NONNULL_N (1 , 5 )
145+ static void mergeRuns (void * array , size_t leftLen , size_t rightLen , size_t size , CompareWithContext compare , void * context ) {
146+ assert (size > 0 );
147+ if (leftLen == 0 || rightLen == 0 || size == 0 )
123148 return ;
124149
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- }
150+ assert (leftLen <= SIZE_MAX / size );
151+ assert (rightLen <= SIZE_MAX / size );
130152
131- // If I were to use only one sorting algorithm for both cases, it would probably be this one:
132- /*
153+ char * p1 = array ;
154+ char * p2 = p1 + leftLen * size ;
155+ char * mid = p2 ;
156+ const char * const end = mid + rightLen * size ;
133157
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- }
158+ for (size_t limit = (leftLen + rightLen ) / 2 ; limit > 0 ; limit -- ) {
159+ // Ensure there is no arithmetic overflow on input.
160+ assert (p1 <= mid );
161+ assert (mid <= p2 );
162+ assert (p2 <= end );
163+
164+ if (p1 >= mid || p2 >= end )
165+ break ;
166+
167+ if (compare (p1 , p2 , context ) <= 0 ) {
168+ p1 += size ;
169+ } else {
170+ p2 += size ;
148171 }
149172 }
150- }
151173
152- */
174+ rotate (p1 , (size_t )(mid - p1 ), (size_t )(p2 - mid ));
175+
176+ leftLen = (size_t )(p1 - (char * )array ) / size ;
177+ rightLen = (size_t )(p2 - mid ) / size ;
178+ mergeRuns (array , leftLen , rightLen , size , compare , context );
153179
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 )
180+ leftLen = (size_t )(mid - p1 ) / size ;
181+ rightLen = (size_t )(end - p2 ) / size ;
182+ mergeRuns (p1 + (p2 - mid ), leftLen , rightLen , size , compare , context );
183+ }
184+
185+ ATTR_NONNULL_N (1 , 5 )
186+ static size_t mergeSortSubarray (void * array , size_t unsortedLen , size_t limit , size_t size , CompareWithContext compare , void * context ) {
187+ assert (size > 0 );
188+ if (size == 0 )
189+ return 0 ;
190+
191+ // The initial level of this function call must set "limit" to 0. Subsequent
192+ // levels of recursion will have "limit" no less than the previous level.
193+
194+ // A run is a sorted subarray. Each recursive call of this function keeps
195+ // the lengths of two runs. At most O(log(n)) lengths of runs will be
196+ // tracked on the call stack.
197+ size_t runLen [3 ] = {0 };
198+ while (unsortedLen > 0 ) {
199+ size_t totalLen = unsortedLen ;
200+ assert (totalLen <= SIZE_MAX / size );
201+ while (true) {
202+ -- unsortedLen ;
203+
204+ const char * p2 = (const char * )array + unsortedLen * size ;
205+ // Ensure there is no arithmetic overflow on input.
206+ assert (p2 > (const char * )array );
207+
208+ if (unsortedLen < limit )
209+ return 0 ;
210+
211+ if (unsortedLen == 0 || compare (p2 - 1 * size , p2 , context ) > 0 ) {
161212 break ;
213+ }
214+ }
215+ runLen [1 ] = totalLen - unsortedLen ;
216+
217+ bool reachesLimit = false;
218+
219+ assert (runLen [2 ] > 0 || runLen [0 ] == 0 );
220+ if (runLen [2 ] > 0 ) {
221+ size_t nextLimit = limit ;
222+ if (unsortedLen > runLen [2 ] + limit ) {
223+ nextLimit = unsortedLen - runLen [2 ];
224+ } else {
225+ reachesLimit = true;
226+ }
227+
228+ runLen [0 ] = mergeSortSubarray (array , unsortedLen , nextLimit , size , compare , context );
229+ unsortedLen -= runLen [0 ];
230+
231+ char * p1 = (char * )array + unsortedLen * size ;
232+ mergeRuns (p1 , runLen [0 ], runLen [1 ], size , compare , context );
233+ runLen [1 ] += runLen [0 ];
234+ runLen [0 ] = 0 ;
162235
163- array [j + 1 ] = array [j ];
164- j -- ;
236+ mergeRuns (p1 , runLen [1 ], runLen [2 ], size , compare , context );
237+ }
238+ runLen [2 ] += runLen [1 ];
239+ runLen [1 ] = 0 ;
240+
241+ if (reachesLimit ) {
242+ break ;
165243 }
166- array [j + 1 ] = t ;
167244 }
245+ return runLen [2 ];
168246}
169247
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 ) );
248+ ATTR_NONNULL
249+ static int Vector_sortCompare ( const void * p1 , const void * p2 , void * context ) {
250+ VectorSortContext * vc = ( VectorSortContext * ) context ;
251+
252+ return vc -> compare ( * ( const void * const * ) p1 , * ( const void * const * ) p2 );
175253}
176254
177- void Vector_insertionSort (Vector * this ) {
178- assert (this -> type -> compare );
255+ ATTR_NONNULL_N (1 )
256+ void Vector_sort (Vector * this , Object_Compare compare , void * context ) {
257+ VectorSortContext vc = {
258+ .compare = compare ? compare : this -> type -> compare ,
259+ .compareContext = context ,
260+ };
261+ assert (vc .compare );
179262 assert (Vector_isConsistent (this ));
180- insertionSort (this -> array , 0 , this -> items - 1 , this -> type -> compare );
263+
264+ (void )mergeSortSubarray (this -> array , this -> items , 0 , sizeof (* this -> array ), Vector_sortCompare , & vc );
265+
181266 assert (Vector_isConsistent (this ));
182267}
183268
0 commit comments