@@ -10,12 +10,19 @@ 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+ } VectorSortContext ;
25+
1926Vector * Vector_new (const ObjectClass * type , bool owner , int size ) {
2027 Vector * this ;
2128
@@ -93,91 +100,167 @@ 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+ // Ensure there is no arithmetic overflow on input.
122+ assert (p1 <= mid );
123+ assert (mid <= p2 );
124+ assert (p2 <= end );
125+
126+ if (p2 >= end ) {
127+ assert (mid < end );
128+ p2 = mid ;
115129 }
130+
131+ if (p1 >= p2 )
132+ break ;
133+
134+ if (p1 >= mid )
135+ mid = p2 ;
136+
137+ swapByte (p1 , p2 );
138+ p1 += 1 ;
139+ p2 += 1 ;
116140 }
117- swap (array , storeIndex , right );
118- return storeIndex ;
119141}
120142
121- static void quickSort (Object * * array , int left , int right , Object_Compare compare ) {
122- if (left >= right )
143+ ATTR_NONNULL_N (1 , 5 )
144+ static void mergeRuns (void * array , size_t leftLen , size_t rightLen , size_t size , CompareWithContext compare , void * context ) {
145+ assert (size > 0 );
146+ if (leftLen == 0 || rightLen == 0 || size == 0 )
123147 return ;
124148
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- }
149+ assert (leftLen <= SIZE_MAX / size );
150+ assert (rightLen <= SIZE_MAX / size );
130151
131- // If I were to use only one sorting algorithm for both cases, it would probably be this one:
132- /*
152+ char * p1 = array ;
153+ char * p2 = p1 + leftLen * size ;
154+ char * mid = p2 ;
155+ const char * const end = mid + rightLen * size ;
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+ for (size_t limit = (leftLen + rightLen ) / 2 ; limit > 0 ; limit -- ) {
158+ // Ensure there is no arithmetic overflow on input.
159+ assert (p1 <= mid );
160+ assert (mid <= p2 );
161+ assert (p2 <= end );
162+
163+ if (p1 >= mid || p2 >= end )
164+ break ;
165+
166+ if (compare (p1 , p2 , context ) <= 0 ) {
167+ p1 += size ;
168+ } else {
169+ p2 += size ;
148170 }
149171 }
150- }
151172
152- */
173+ rotate (p1 , (size_t )(mid - p1 ), (size_t )(p2 - mid ));
174+
175+ leftLen = (size_t )(p1 - (char * )array ) / size ;
176+ rightLen = (size_t )(p2 - mid ) / size ;
177+ mergeRuns (array , leftLen , rightLen , size , compare , context );
153178
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 )
179+ leftLen = (size_t )(mid - p1 ) / size ;
180+ rightLen = (size_t )(end - p2 ) / size ;
181+ mergeRuns (p1 + (p2 - mid ), leftLen , rightLen , size , compare , context );
182+ }
183+
184+ ATTR_NONNULL_N (1 , 5 )
185+ static size_t mergeSortSubarray (void * array , size_t unsortedLen , size_t limit , size_t size , CompareWithContext compare , void * context ) {
186+ assert (size > 0 );
187+ if (size == 0 )
188+ return 0 ;
189+
190+ // The initial level of this function call must set "limit" to 0. Subsequent
191+ // levels of recursion will have "limit" no less than the previous level.
192+
193+ // A run is a sorted subarray. Each recursive call of this function keeps
194+ // the lengths of two runs. At most O(log(n)) lengths of runs will be
195+ // tracked on the call stack.
196+ size_t runLen [3 ] = {0 };
197+ while (unsortedLen > 0 ) {
198+ size_t totalLen = unsortedLen ;
199+ assert (totalLen <= SIZE_MAX / size );
200+ while (true) {
201+ -- unsortedLen ;
202+
203+ const char * p2 = (const char * )array + unsortedLen * size ;
204+ // Ensure there is no arithmetic overflow on input.
205+ assert (p2 > (const char * )array );
206+
207+ if (unsortedLen < limit )
208+ return 0 ;
209+
210+ if (unsortedLen == 0 || compare (p2 - 1 * size , p2 , context ) > 0 ) {
161211 break ;
212+ }
213+ }
214+ runLen [1 ] = totalLen - unsortedLen ;
215+
216+ bool reachesLimit = false;
217+
218+ assert (runLen [2 ] > 0 || runLen [0 ] == 0 );
219+ if (runLen [2 ] > 0 ) {
220+ size_t nextLimit = limit ;
221+ if (unsortedLen > runLen [2 ] + limit ) {
222+ nextLimit = unsortedLen - runLen [2 ];
223+ } else {
224+ reachesLimit = true;
225+ }
226+
227+ runLen [0 ] = mergeSortSubarray (array , unsortedLen , nextLimit , size , compare , context );
228+ unsortedLen -= runLen [0 ];
229+
230+ char * p1 = (char * )array + unsortedLen * size ;
231+ mergeRuns (p1 , runLen [0 ], runLen [1 ], size , compare , context );
232+ runLen [1 ] += runLen [0 ];
233+ runLen [0 ] = 0 ;
162234
163- array [j + 1 ] = array [j ];
164- j -- ;
235+ mergeRuns (p1 , runLen [1 ], runLen [2 ], size , compare , context );
236+ }
237+ runLen [2 ] += runLen [1 ];
238+ runLen [1 ] = 0 ;
239+
240+ if (reachesLimit ) {
241+ break ;
165242 }
166- array [j + 1 ] = t ;
167243 }
244+ return runLen [2 ];
168245}
169246
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 ) );
247+ ATTR_NONNULL
248+ static int Vector_sortCompare ( const void * p1 , const void * p2 , void * context ) {
249+ VectorSortContext * vc = ( VectorSortContext * ) context ;
250+
251+ return vc -> compare ( * ( const void * const * ) p1 , * ( const void * const * ) p2 );
175252}
176253
177- void Vector_insertionSort (Vector * this ) {
178- assert (this -> type -> compare );
254+ ATTR_NONNULL_N (1 )
255+ void Vector_sort (Vector * this , Object_Compare compare ) {
256+ VectorSortContext vc = {
257+ .compare = compare ? compare : this -> type -> compare ,
258+ };
259+ assert (vc .compare );
179260 assert (Vector_isConsistent (this ));
180- insertionSort (this -> array , 0 , this -> items - 1 , this -> type -> compare );
261+
262+ (void )mergeSortSubarray (this -> array , this -> items , 0 , sizeof (* this -> array ), Vector_sortCompare , & vc );
263+
181264 assert (Vector_isConsistent (this ));
182265}
183266
0 commit comments