1414#include <SDL3/SDL_main.h>
1515#include <SDL3/SDL_test.h>
1616
17+ /* Required to test non-aligned sort */
18+ typedef char non_word_value [sizeof (int ) * 2 ];
19+
20+ typedef struct
21+ {
22+ size_t len ;
23+ size_t item_size ;
24+ void * aligned ;
25+ void * unaligned ;
26+ } test_buffer ;
27+
1728static int a_global_var = 77 ;
29+ static int qsort_is_broken ;
30+ static SDLTest_RandomContext rndctx ;
1831
1932static int SDLCALL
2033num_compare (const void * _a , const void * _b )
@@ -24,54 +37,238 @@ num_compare(const void *_a, const void *_b)
2437 return (a < b ) ? -1 : ((a > b ) ? 1 : 0 );
2538}
2639
40+ static int SDLCALL
41+ num_compare_non_transitive (const void * _a , const void * _b )
42+ {
43+ const int a = * ((const int * )_a );
44+ const int b = * ((const int * )_b );
45+ return (a < b ) ? 0 : 1 ;
46+ }
47+
2748static int SDLCALL
2849num_compare_r (void * userdata , const void * a , const void * b )
2950{
3051 if (userdata != & a_global_var ) {
3152 SDL_Log ("Uhoh, invalid userdata during qsort!" );
53+ qsort_is_broken = 1 ;
3254 }
3355 return num_compare (a , b );
3456}
3557
58+ /* Will never return 0 */
59+ static int SDLCALL
60+ num_compare_random_any (const void * a , const void * b )
61+ {
62+ (void )(a );
63+ (void )(b );
64+ return (SDLTest_Random (& rndctx ) > ((Uint32 )SDL_MAX_SINT32 )) ? 1 : -1 ;
65+ }
66+
67+ static int SDLCALL
68+ num_compare_non_word (const void * _a , const void * _b )
69+ {
70+ const int a = (int )(((const char * )_a )[0 ]);
71+ const int b = (int )(((const char * )_b )[0 ]);
72+ return num_compare (& a , & b );
73+ }
74+
75+ static int SDLCALL
76+ num_compare_non_word_r (void * userdata , const void * _a , const void * _b )
77+ {
78+ const int a = (int )(((const char * )_a )[0 ]);
79+ const int b = (int )(((const char * )_b )[0 ]);
80+ return num_compare_r (userdata , & a , & b );
81+ }
82+
83+ static int SDLCALL
84+ num_compare_non_word_non_transitive (const void * _a , const void * _b )
85+ {
86+ const int a = (int )(((char * )_a )[0 ]);
87+ const int b = (int )(((char * )_b )[0 ]);
88+ return num_compare_non_transitive (& a , & b );
89+ }
90+
3691static void
37- test_sort ( const char * desc , int * nums , const int arraylen )
92+ alloc_buffer ( test_buffer * buffer , size_t size , size_t len )
3893{
39- static int nums_copy [1024 * 100 ];
40- int i ;
41- int prev ;
94+ uintptr_t base ;
4295
43- SDL_assert (SDL_arraysize (nums_copy ) >= arraylen );
96+ SDL_memset (buffer , 0 , sizeof (* buffer ));
97+ buffer -> len = len ;
98+ buffer -> item_size = size ;
4499
45- SDL_Log ("test: %s arraylen=%d" , desc , arraylen );
100+ if (len == 0 ) {
101+ return ;
102+ }
46103
47- SDL_memcpy (nums_copy , nums , arraylen * sizeof (* nums ));
104+ buffer -> aligned = SDL_malloc ((len + 1 ) * size + sizeof (char ));
105+ base = (uintptr_t )(buffer -> aligned );
106+ /* Should be aligned */
107+ SDL_assert (((base | size ) & (sizeof (int ) - 1 )) == 0 );
48108
49- SDL_qsort ( nums , arraylen , sizeof (nums [ 0 ]), num_compare );
50- SDL_qsort_r ( nums_copy , arraylen , sizeof ( nums [ 0 ]), num_compare_r , & a_global_var );
109+ buffer -> unaligned = ( void * )( base + sizeof (char ) );
110+ }
51111
52- prev = nums [0 ];
53- for (i = 1 ; i < arraylen ; i ++ ) {
112+ static void
113+ check_sort (const int * nums , const int * r_nums , int numlen ) {
114+ int i ;
115+ int prev = 0 ;
116+
117+ if (numlen > 0 ) {
118+ prev = nums [0 ];
119+ }
120+ for (i = 1 ; i < numlen ; i ++ ) {
54121 const int val = nums [i ];
55- const int val2 = nums_copy [i ];
122+ const int val2 = r_nums [i ];
56123 if ((val < prev ) || (val != val2 )) {
57124 SDL_Log ("sort is broken!" );
58- return ;
125+ qsort_is_broken = 1 ;
126+ break ;
59127 }
60128 prev = val ;
61129 }
62130}
63131
64- int main (int argc , char * argv [])
132+ static void
133+ test_sort (const char * desc , const int * nums , int numlen )
134+ {
135+ test_buffer buffer ;
136+ test_buffer buffer_r ;
137+
138+ alloc_buffer (& buffer , sizeof (nums [0 ]), numlen );
139+ alloc_buffer (& buffer_r , sizeof (nums [0 ]), numlen );
140+
141+ SDL_Log ("test: %s bufferlen=%d" , desc , (int )(buffer .len ));
142+
143+ /* Test aligned sort */
144+ if (buffer .aligned != NULL ) {
145+ /* memcpy with NULL is UB */
146+ SDL_memcpy (buffer .aligned , nums , numlen * sizeof (nums [0 ]));
147+ SDL_memcpy (buffer_r .aligned , nums , numlen * sizeof (nums [0 ]));
148+ }
149+ SDL_qsort (buffer .aligned , numlen , sizeof (nums [0 ]), num_compare );
150+ SDL_qsort_r (buffer_r .aligned , numlen , sizeof (nums [0 ]), num_compare_r , & a_global_var );
151+ check_sort (buffer .aligned , buffer_r .aligned , numlen );
152+
153+ /* We can't test unaligned int sort because it's UB. */
154+
155+ SDL_free (buffer_r .aligned );
156+ SDL_free (buffer .aligned );
157+ }
158+
159+ static void
160+ check_non_word_sort (non_word_value * nums , non_word_value * r_nums , int numlen ) {
161+ int i ;
162+ char prev ;
163+
164+ if (numlen > 0 ) {
165+ prev = nums [0 ][0 ];
166+ }
167+ for (i = 1 ; i < numlen ; i ++ ) {
168+ const char val = nums [i ][0 ];
169+ const char val2 = r_nums [i ][0 ];
170+ if ((val < prev ) || (val != val2 )) {
171+ SDL_Log ("sort is broken!" );
172+ qsort_is_broken = 1 ;
173+ break ;
174+ }
175+ prev = val ;
176+ }
177+ }
178+
179+ static void
180+ test_sort_non_word (const char * desc , non_word_value * nums , int numlen )
65181{
66- static int nums [1024 * 100 ];
67- static const int itervals [] = { SDL_arraysize (nums ), 12 };
182+ test_buffer buffer ;
183+ test_buffer buffer_r ;
184+
185+ alloc_buffer (& buffer , sizeof (nums [0 ]), numlen );
186+ alloc_buffer (& buffer_r , sizeof (nums [0 ]), numlen );
187+
188+ SDL_Log ("test: %s non-word numlen=%d" , desc , numlen );
189+
190+ /* Test aligned sort */
191+ if (buffer .aligned != NULL ) {
192+ SDL_memcpy (buffer .aligned , nums , numlen * sizeof (nums [0 ]));
193+ SDL_memcpy (buffer_r .aligned , nums , numlen * sizeof (nums [0 ]));
194+ }
195+ SDL_qsort (buffer .aligned , numlen , sizeof (nums [0 ]), num_compare_non_word );
196+ SDL_qsort_r (buffer_r .aligned , numlen , sizeof (nums [0 ]), num_compare_non_word_r , & a_global_var );
197+ check_non_word_sort (buffer .aligned , buffer_r .aligned , numlen );
198+
199+ /* Test non-aligned sort */
200+ if (buffer .unaligned != NULL ) {
201+ SDL_memcpy (buffer .unaligned , nums , numlen * sizeof (nums [0 ]));
202+ SDL_memcpy (buffer_r .unaligned , nums , numlen * sizeof (nums [0 ]));
203+ }
204+ SDL_qsort (buffer .unaligned , numlen , sizeof (nums [0 ]), num_compare_non_word );
205+ SDL_qsort_r (buffer_r .unaligned , numlen , sizeof (nums [0 ]), num_compare_non_word_r , & a_global_var );
206+ check_non_word_sort (buffer .unaligned , buffer_r .unaligned , numlen );
207+
208+ SDL_free (buffer_r .aligned );
209+ SDL_free (buffer .aligned );
210+ }
211+
212+ static void
213+ test_sort_non_transitive (int numlen ) {
68214 int i ;
69- int iteration ;
70- SDLTest_RandomContext rndctx ;
71- SDLTest_CommonState * state ;
215+ test_buffer buffer ;
216+ test_buffer non_word_buffer ;
217+ int * nums ;
218+ non_word_value * non_word_nums ;
219+
220+ SDL_Log ("test: non-transitive numlen=%d" , (numlen ));
221+
222+ alloc_buffer (& buffer , sizeof (nums [0 ]), numlen );
223+ alloc_buffer (& non_word_buffer , sizeof (non_word_nums [0 ]), numlen );
224+
225+ /* Test aligned sort */
226+ nums = buffer .aligned ;
227+ non_word_nums = non_word_buffer .aligned ;
228+ for (i = 0 ; i < numlen ; i ++ ) {
229+ nums [i ] = numlen - i ;
230+ non_word_nums [i ][0 ] = (char )nums [i ];
231+ }
232+
233+ SDL_qsort (nums , numlen , sizeof (nums [0 ]), num_compare_non_transitive );
234+ SDL_qsort (non_word_nums , numlen , sizeof (non_word_nums [0 ]), num_compare_non_word_non_transitive );
235+
236+ /* What's inside doesn't matter for random comparison */
237+ SDL_qsort (nums , numlen , sizeof (nums [0 ]), num_compare_random_any );
238+ SDL_qsort (non_word_nums , numlen , sizeof (non_word_nums [0 ]), num_compare_random_any );
239+
240+ /* Test non-aligned sort */
241+ non_word_nums = non_word_buffer .unaligned ;
242+ for (i = 0 ; i < numlen ; i ++ ) {
243+ non_word_nums [i ][0 ] = (char )(numlen - i );
244+ }
245+
246+ SDL_qsort (non_word_nums , numlen , sizeof (non_word_nums [0 ]), num_compare_non_word_non_transitive );
247+ SDL_qsort (non_word_nums , numlen , sizeof (non_word_nums [0 ]), num_compare_random_any );
248+
249+ SDL_free (buffer .aligned );
250+ SDL_free (non_word_buffer .aligned );
251+ }
252+
253+ int main (int argc , char * argv [])
254+ {
255+ static int nums [1024 * 128 ];
256+ static non_word_value non_word_nums [1024 * 128 ];
257+
258+ SDL_assert (SDL_arraysize (nums ) == SDL_arraysize (non_word_nums ));
259+
260+ /* Test truncation points */
261+ static const int itervals [] = { 0 , 12 , 15 , 12 * sizeof (int ), SDL_arraysize (nums ) };
262+ /* Non-transitive sorting consumes much more CPU, use smaller value */
263+ static const int itervals_non_transitive [] = { 0 , 12 , 15 , 12 * sizeof (int ), 16384 };
264+
265+ int i = 0 ;
266+ int iteration = 0 ;
267+ SDLTest_CommonState * state = NULL ;
72268 int seed_seen = 0 ;
73269
74270 SDL_zero (rndctx );
271+ qsort_is_broken = 0 ;
75272
76273 /* Initialize test framework */
77274 state = SDLTest_CommonCreateState (argv , 0 );
@@ -123,27 +320,43 @@ int main(int argc, char *argv[])
123320
124321 for (i = 0 ; i < arraylen ; i ++ ) {
125322 nums [i ] = i ;
323+ non_word_nums [i ][0 ] = (char )i ;
126324 }
127325 test_sort ("already sorted" , nums , arraylen );
326+ test_sort_non_word ("already_sorted" , non_word_nums , arraylen );
128327
129- for (i = 0 ; i < arraylen ; i ++ ) {
130- nums [i ] = i ;
328+ if (arraylen > 0 ) {
329+ for (i = 0 ; i < arraylen ; i ++ ) {
330+ nums [i ] = i ;
331+ non_word_nums [i ][0 ] = (char )i ;
332+ }
333+ nums [arraylen - 1 ] = -1 ;
334+ test_sort ("already sorted except last element" , nums , arraylen );
335+ test_sort_non_word ("already sorted except last element" , non_word_nums , arraylen );
131336 }
132- nums [arraylen - 1 ] = -1 ;
133- test_sort ("already sorted except last element" , nums , arraylen );
134337
135338 for (i = 0 ; i < arraylen ; i ++ ) {
136339 nums [i ] = (arraylen - 1 ) - i ;
340+ non_word_nums [i ][0 ] = (char )i ;
137341 }
138342 test_sort ("reverse sorted" , nums , arraylen );
343+ test_sort_non_word ("reverse sorted" , non_word_nums , arraylen );
139344
140345 for (i = 0 ; i < arraylen ; i ++ ) {
141346 nums [i ] = SDLTest_RandomInt (& rndctx );
347+ non_word_nums [i ][0 ] = (char )i ;
142348 }
143349 test_sort ("random sorted" , nums , arraylen );
350+ test_sort_non_word ("random sorted" , non_word_nums , arraylen );
351+ }
352+
353+ for (iteration = 0 ; iteration < SDL_arraysize (itervals_non_transitive ); iteration ++ ) {
354+ const int arraylen = itervals_non_transitive [iteration ];
355+
356+ test_sort_non_transitive (arraylen );
144357 }
145358
146359 SDLTest_CommonDestroyState (state );
147360
148- return 0 ;
361+ return qsort_is_broken ;
149362}
0 commit comments