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 (const  non_word_value  * nums , const  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 , const  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