فهرست منبع

Simplified SDL random function names and added thread-safe versions

Sam Lantinga 10 ماه پیش
والد
کامیت
96f2f23240

+ 75 - 23
include/SDL3/SDL_stdinc.h

@@ -1273,16 +1273,20 @@ extern SDL_DECLSPEC int SDLCALL SDL_vasprintf(char **strp, SDL_PRINTF_FORMAT_STR
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_rand_n
- * \sa SDL_rand_float
- * \sa SDL_rand_bits
+ * \sa SDL_rand
+ * \sa SDL_randf
  */
 extern SDL_DECLSPEC void SDLCALL SDL_srand(Uint64 seed);
 
 /**
- * Generates 32 pseudo-random bits.
+ * Generate a pseudo-random number less than n for positive n
  *
- * You likely want to use SDL_rand_n() to get a psuedo-randum number instead.
+ * The method used is faster and of better quality than `rand() % n`. Odds are
+ * roughly 99.9% even for n = 1 million. Evenness is better for smaller n, and
+ * much worse as n gets bigger.
+ *
+ * Example: to simulate a d6 use `SDL_rand(6) + 1` The +1 converts 0..5 to
+ * 1..6
  *
  * If you want reproducible output, be sure to initialize with SDL_srand()
  * first.
@@ -1293,51 +1297,73 @@ extern SDL_DECLSPEC void SDLCALL SDL_srand(Uint64 seed);
  * libraries available with different characteristics and you should pick one
  * of those to meet any serious needs.
  *
- * \returns a random value in the range of [0-SDL_MAX_UINT32].
+ * \param n the number of possible outcomes. n must be positive.
+ * \returns a random value in the range of [0 .. n-1].
+ *
+ * \threadsafety All calls should be made from a single thread
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_srand
+ * \sa SDL_randf
+ */
+extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand(Sint32 n);
+
+/**
+ * Generate a uniform pseudo-random floating point number less than 1.0
+ *
+ * If you want reproducible output, be sure to initialize with SDL_srand()
+ * first.
+ *
+ * There are no guarantees as to the quality of the random sequence produced,
+ * and this should not be used for security (cryptography, passwords) or where
+ * money is on the line (loot-boxes, casinos). There are many random number
+ * libraries available with different characteristics and you should pick one
+ * of those to meet any serious needs.
+ *
+ * \returns a random value in the range of [0.0, 1.0).
  *
  * \threadsafety All calls should be made from a single thread
  *
  * \since This function is available since SDL 3.0.0.
  *
  * \sa SDL_srand
- * \sa SDL_rand_n
- * \sa SDL_rand_float
+ * \sa SDL_rand
  */
-extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits(void);
+extern SDL_DECLSPEC float SDLCALL SDL_randf(void);
 
 /**
- * Generates a pseudo-random number less than n for positive n
+ * Generate a pseudo-random number less than n for positive n
  *
  * The method used is faster and of better quality than `rand() % n`. Odds are
  * roughly 99.9% even for n = 1 million. Evenness is better for smaller n, and
  * much worse as n gets bigger.
  *
- * Example: to simulate a d6 use `SDL_rand_n(6) + 1` The +1 converts 0..5 to
+ * Example: to simulate a d6 use `SDL_rand_r(state, 6) + 1` The +1 converts 0..5 to
  * 1..6
  *
- * If you want reproducible output, be sure to initialize with SDL_srand()
- * first.
- *
  * There are no guarantees as to the quality of the random sequence produced,
  * and this should not be used for security (cryptography, passwords) or where
  * money is on the line (loot-boxes, casinos). There are many random number
  * libraries available with different characteristics and you should pick one
  * of those to meet any serious needs.
  *
+ * \param state a pointer to the current random number state, this may not be NULL.
  * \param n the number of possible outcomes. n must be positive.
  * \returns a random value in the range of [0 .. n-1].
  *
- * \threadsafety All calls should be made from a single thread
+ * \threadsafety This function is thread-safe, as long as the state pointer isn't shared between threads.
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_srand
- * \sa SDL_rand_float
+ * \sa SDL_rand
+ * \sa SDL_rand_bits_r
+ * \sa SDL_randf_r
  */
-extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_n(Sint32 n);
+extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_r(Uint64 *state, Sint32 n);
 
 /**
- * Generates a uniform pseudo-random floating point number less than 1.0
+ * Generate a uniform pseudo-random floating point number less than 1.0
  *
  * If you want reproducible output, be sure to initialize with SDL_srand()
  * first.
@@ -1348,16 +1374,42 @@ extern SDL_DECLSPEC Sint32 SDLCALL SDL_rand_n(Sint32 n);
  * libraries available with different characteristics and you should pick one
  * of those to meet any serious needs.
  *
+ * \param state a pointer to the current random number state, this may not be NULL.
  * \returns a random value in the range of [0.0, 1.0).
  *
- * \threadsafety All calls should be made from a single thread
+ * \threadsafety This function is thread-safe, as long as the state pointer isn't shared between threads.
  *
  * \since This function is available since SDL 3.0.0.
  *
- * \sa SDL_srand
- * \sa SDL_rand_n
+ * \sa SDL_rand_bits_r
+ * \sa SDL_rand_r
+ * \sa SDL_randf
+ */
+extern SDL_DECLSPEC float SDLCALL SDL_randf_r(Uint64 *state);
+
+/**
+ * Generate 32 pseudo-random bits.
+ *
+ * You likely want to use SDL_rand_r() to get a psuedo-randum number instead.
+ *
+ * There are no guarantees as to the quality of the random sequence produced,
+ * and this should not be used for security (cryptography, passwords) or where
+ * money is on the line (loot-boxes, casinos). There are many random number
+ * libraries available with different characteristics and you should pick one
+ * of those to meet any serious needs.
+ *
+ * \param state a pointer to the current random number state, this may not be NULL.
+ * \returns a random value in the range of [0-SDL_MAX_UINT32].
+ *
+ * \threadsafety This function is thread-safe, as long as the state pointer isn't shared between threads.
+ *
+ * \since This function is available since SDL 3.0.0.
+ *
+ * \sa SDL_rand_r
+ * \sa SDL_randf_r
  */
-extern SDL_DECLSPEC float SDLCALL SDL_rand_float(void);
+extern SDL_DECLSPEC Uint32 SDLCALL SDL_rand_bits_r(Uint64 *state);
+
 
 #ifndef SDL_PI_D
 #define SDL_PI_D   3.141592653589793238462643383279502884       /**< pi (double) */

+ 5 - 3
src/dynapi/SDL_dynapi.sym

@@ -961,9 +961,11 @@ SDL3_0.0.0 {
     SDL_powf;
     SDL_qsort;
     SDL_qsort_r;
-    SDL_rand_bits;
-    SDL_rand_float;
-    SDL_rand_n;  
+    SDL_rand;  
+    SDL_rand_bits_r;
+    SDL_rand_r;
+    SDL_randf;
+    SDL_randf_r;
     SDL_realloc;
     SDL_round;
     SDL_roundf;

+ 5 - 3
src/dynapi/SDL_dynapi_overrides.h

@@ -986,9 +986,11 @@
 #define SDL_powf SDL_powf_REAL
 #define SDL_qsort SDL_qsort_REAL
 #define SDL_qsort_r SDL_qsort_r_REAL
-#define SDL_rand_bits SDL_rand_bits_REAL
-#define SDL_rand_float SDL_rand_float_REAL
-#define SDL_rand_n SDL_rand_n_REAL
+#define SDL_rand SDL_rand_REAL
+#define SDL_rand_bits_r SDL_rand_bits_r_REAL
+#define SDL_rand_r SDL_rand_r_REAL
+#define SDL_randf SDL_randf_REAL
+#define SDL_randf_r SDL_randf_r_REAL
 #define SDL_realloc SDL_realloc_REAL
 #define SDL_round SDL_round_REAL
 #define SDL_roundf SDL_roundf_REAL

+ 5 - 3
src/dynapi/SDL_dynapi_procs.h

@@ -995,9 +995,11 @@ SDL_DYNAPI_PROC(double,SDL_pow,(double a, double b),(a,b),return)
 SDL_DYNAPI_PROC(float,SDL_powf,(float a, float b),(a,b),return)
 SDL_DYNAPI_PROC(void,SDL_qsort,(void *a, size_t b, size_t c, SDL_CompareCallback d),(a,b,c,d),)
 SDL_DYNAPI_PROC(void,SDL_qsort_r,(void *a, size_t b, size_t c, SDL_CompareCallback_r d, void *e),(a,b,c,d,e),)
-SDL_DYNAPI_PROC(Uint32,SDL_rand_bits,(void),(),return)
-SDL_DYNAPI_PROC(float,SDL_rand_float,(void),(),return)
-SDL_DYNAPI_PROC(Sint32,SDL_rand_n,(Sint32 a),(a),return)
+SDL_DYNAPI_PROC(Sint32,SDL_rand,(Sint32 a),(a),return)
+SDL_DYNAPI_PROC(Uint32,SDL_rand_bits_r,(Uint64 *a),(a),return)
+SDL_DYNAPI_PROC(Sint32,SDL_rand_r,(Uint64 *a, Sint32 b),(a,b),return)
+SDL_DYNAPI_PROC(float,SDL_randf,(void),(),return)
+SDL_DYNAPI_PROC(float,SDL_randf_r,(Uint64 *a),(a),return)
 SDL_DYNAPI_PROC(void*,SDL_realloc,(void *a, size_t b),(a,b),return)
 SDL_DYNAPI_PROC(double,SDL_round,(double a),(a),return)
 SDL_DYNAPI_PROC(float,SDL_roundf,(float a),(a),return)

+ 27 - 8
src/stdlib/SDL_random.c

@@ -34,12 +34,30 @@ void SDL_srand(Uint64 seed)
     SDL_rand_initialized = SDL_TRUE;
 }
 
-Uint32 SDL_rand_bits(void)
+Sint32 SDL_rand(Sint32 n)
 {
     if (!SDL_rand_initialized) {
         SDL_srand(0);
     }
 
+    return SDL_rand_r(&SDL_rand_state, n);
+}
+
+float SDL_randf(void)
+{
+    if (!SDL_rand_initialized) {
+        SDL_srand(0);
+    }
+
+    return SDL_randf_r(&SDL_rand_state);
+}
+
+Uint32 SDL_rand_bits_r(Uint64 *state)
+{
+    if (!state) {
+        return 0;
+    }
+
     // The C and A parameters of this LCG have been chosen based on hundreds
     // of core-hours of testing with PractRand and TestU01's Crush.
     // Using a 32-bit A improves performance on 32-bit architectures.
@@ -55,13 +73,13 @@ Uint32 SDL_rand_bits(void)
     // Softw Pract Exper. 2022;52(2):443-458. doi: 10.1002/spe.3030
     // https://arxiv.org/abs/2001.05304v2
 
-    SDL_rand_state = SDL_rand_state * 0xff1cd035ul + 0x05;
+    *state = *state * 0xff1cd035ul + 0x05;
 
     // Only return top 32 bits because they have a longer period
-    return (Uint32)(SDL_rand_state >> 32);
+    return (Uint32)(*state >> 32);
 }
 
-Sint32 SDL_rand_n(Sint32 n)
+Sint32 SDL_rand_r(Uint64 *state, Sint32 n)
 {
     // Algorithm: get 32 bits from SDL_rand_bits() and treat it as a 0.32 bit
     // fixed point number. Multiply by the 31.0 bit n to get a 31.32 bit
@@ -70,18 +88,19 @@ Sint32 SDL_rand_n(Sint32 n)
     if (n < 0) {
         // The algorithm looks like it works for numbers < 0 but it has an
         // infintesimal chance of returning a value out of range.
-        // Returning -SDL_rand_n(abs(n)) blows up at INT_MIN instead.
+        // Returning -SDL_rand(abs(n)) blows up at INT_MIN instead.
         // It's easier to just say no.
         return 0;
     }
 
     // On 32-bit arch, the compiler will optimize to a single 32-bit multiply
-    Uint64 val = (Uint64)SDL_rand_bits() * n;
+    Uint64 val = (Uint64)SDL_rand_bits_r(state) * n;
     return (Sint32)(val >> 32);
 }
 
-float SDL_rand_float(void)
+float SDL_randf_r(Uint64 *state)
 {
     // Note: its using 24 bits because float has 23 bits significand + 1 implicit bit
-    return (SDL_rand_bits() >> (32 - 24)) * 0x1p-24f;
+    return (SDL_rand_bits_r(state) >> (32 - 24)) * 0x1p-24f;
 }
+

+ 1 - 1
test/checkkeysthreads.c

@@ -231,7 +231,7 @@ static int SDLCALL ping_thread(void *ptr)
         sdlevent.type = SDL_EVENT_KEY_DOWN;
         sdlevent.key.key = SDLK_1;
         SDL_PushEvent(&sdlevent);
-        SDL_Delay(1000 + SDL_rand_n(1000));
+        SDL_Delay(1000 + SDL_rand(1000));
     }
     return cnt;
 }

+ 10 - 10
test/testdraw.c

@@ -72,8 +72,8 @@ static void DrawPoints(SDL_Renderer *renderer)
         SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color,
                                (Uint8)current_color, (Uint8)current_alpha);
 
-        x = (float)SDL_rand_n(viewport.w);
-        y = (float)SDL_rand_n(viewport.h);
+        x = (float)SDL_rand(viewport.w);
+        y = (float)SDL_rand(viewport.h);
         SDL_RenderPoint(renderer, x, y);
     }
 }
@@ -120,10 +120,10 @@ static void DrawLines(SDL_Renderer *renderer)
             SDL_RenderLine(renderer, 0.0f, (float)(viewport.h / 2), (float)(viewport.w - 1), (float)(viewport.h / 2));
             SDL_RenderLine(renderer, (float)(viewport.w / 2), 0.0f, (float)(viewport.w / 2), (float)(viewport.h - 1));
         } else {
-            x1 = (float)(SDL_rand_n(viewport.w * 2) - viewport.w);
-            x2 = (float)(SDL_rand_n(viewport.w * 2) - viewport.w);
-            y1 = (float)(SDL_rand_n(viewport.h * 2) - viewport.h);
-            y2 = (float)(SDL_rand_n(viewport.h * 2) - viewport.h);
+            x1 = (float)(SDL_rand(viewport.w * 2) - viewport.w);
+            x2 = (float)(SDL_rand(viewport.w * 2) - viewport.w);
+            y1 = (float)(SDL_rand(viewport.h * 2) - viewport.h);
+            y2 = (float)(SDL_rand(viewport.h * 2) - viewport.h);
             SDL_RenderLine(renderer, x1, y1, x2, y2);
         }
     }
@@ -165,10 +165,10 @@ static void DrawRects(SDL_Renderer *renderer)
         SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color,
                                (Uint8)current_color, (Uint8)current_alpha);
 
-        rect.w = (float)SDL_rand_n(viewport.h / 2);
-        rect.h = (float)SDL_rand_n(viewport.h / 2);
-        rect.x = (float)((SDL_rand_n(viewport.w * 2) - viewport.w) - (rect.w / 2));
-        rect.y = (float)((SDL_rand_n(viewport.h * 2) - viewport.h) - (rect.h / 2));
+        rect.w = (float)SDL_rand(viewport.h / 2);
+        rect.h = (float)SDL_rand(viewport.h / 2);
+        rect.x = (float)((SDL_rand(viewport.w * 2) - viewport.w) - (rect.w / 2));
+        rect.y = (float)((SDL_rand(viewport.h * 2) - viewport.h) - (rect.h / 2));
         SDL_RenderFillRect(renderer, &rect);
     }
 }

+ 4 - 4
test/testffmpeg.c

@@ -1485,15 +1485,15 @@ int main(int argc, char *argv[])
     SDL_Rect viewport;
     SDL_GetRenderViewport(renderer, &viewport);
     for (i = 0; i < num_sprites; ++i) {
-        positions[i].x = (float)SDL_rand_n(viewport.w - sprite_w);
-        positions[i].y = (float)SDL_rand_n(viewport.h - sprite_h);
+        positions[i].x = (float)SDL_rand(viewport.w - sprite_w);
+        positions[i].y = (float)SDL_rand(viewport.h - sprite_h);
         positions[i].w = (float)sprite_w;
         positions[i].h = (float)sprite_h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f || velocities[i].y == 0.f) {
-            velocities[i].x = (float)(SDL_rand_n(2 + 1) - 1);
-            velocities[i].y = (float)(SDL_rand_n(2 + 1) - 1);
+            velocities[i].x = (float)(SDL_rand(2 + 1) - 1);
+            velocities[i].y = (float)(SDL_rand(2 + 1) - 1);
         }
     }
 

+ 10 - 10
test/testintersections.c

@@ -74,8 +74,8 @@ static void DrawPoints(SDL_Renderer *renderer)
         SDL_SetRenderDrawColor(renderer, 255, (Uint8)current_color,
                                (Uint8)current_color, (Uint8)current_alpha);
 
-        x = (float)SDL_rand_n(viewport.w);
-        y = (float)SDL_rand_n(viewport.h);
+        x = (float)SDL_rand(viewport.w);
+        y = (float)SDL_rand(viewport.h);
         SDL_RenderPoint(renderer, x, y);
     }
 }
@@ -231,20 +231,20 @@ static void loop(void *arg)
                 break;
             case SDLK_l:
                 add_line(
-                    (float)SDL_rand_n(640),
-                    (float)SDL_rand_n(480),
-                    (float)SDL_rand_n(640),
-                    (float)SDL_rand_n(480));
+                    (float)SDL_rand(640),
+                    (float)SDL_rand(480),
+                    (float)SDL_rand(640),
+                    (float)SDL_rand(480));
                 break;
             case SDLK_R:
                 num_rects = 0;
                 break;
             case SDLK_r:
                 add_rect(
-                    (float)SDL_rand_n(640),
-                    (float)SDL_rand_n(480),
-                    (float)SDL_rand_n(640),
-                    (float)SDL_rand_n(480));
+                    (float)SDL_rand(640),
+                    (float)SDL_rand(480),
+                    (float)SDL_rand(640),
+                    (float)SDL_rand(480));
                 break;
             default:
                 break;

+ 4 - 4
test/testnative.c

@@ -189,15 +189,15 @@ int main(int argc, char *argv[])
         quit(2);
     }
     for (i = 0; i < NUM_SPRITES; ++i) {
-        positions[i].x = (float)(SDL_rand_n(window_w - (int)sprite_w));
-        positions[i].y = (float)(SDL_rand_n(window_h - (int)sprite_h));
+        positions[i].x = (float)(SDL_rand(window_w - (int)sprite_w));
+        positions[i].y = (float)(SDL_rand(window_h - (int)sprite_h));
         positions[i].w = sprite_w;
         positions[i].h = sprite_h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
-            velocities[i].x = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED);
-            velocities[i].y = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED);
+            velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
+            velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
         }
     }
 

+ 4 - 4
test/testspriteminimal.c

@@ -135,15 +135,15 @@ int main(int argc, char *argv[])
 
     /* Initialize the sprite positions */
     for (i = 0; i < NUM_SPRITES; ++i) {
-        positions[i].x = (float)SDL_rand_n(WINDOW_WIDTH - sprite_w);
-        positions[i].y = (float)SDL_rand_n(WINDOW_HEIGHT - sprite_h);
+        positions[i].x = (float)SDL_rand(WINDOW_WIDTH - sprite_w);
+        positions[i].y = (float)SDL_rand(WINDOW_HEIGHT - sprite_h);
         positions[i].w = (float)sprite_w;
         positions[i].h = (float)sprite_h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
-            velocities[i].x = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED);
-            velocities[i].y = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED);
+            velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
+            velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
         }
     }
 

+ 4 - 4
test/testwaylandcustom.c

@@ -97,15 +97,15 @@ static int InitSprites(void)
     }
 
     for (int i = 0; i < NUM_SPRITES; ++i) {
-        positions[i].x = (float)SDL_rand_n(WINDOW_WIDTH - sprite_w);
-        positions[i].y = (float)SDL_rand_n(WINDOW_HEIGHT - sprite_h);
+        positions[i].x = (float)SDL_rand(WINDOW_WIDTH - sprite_w);
+        positions[i].y = (float)SDL_rand(WINDOW_HEIGHT - sprite_h);
         positions[i].w = (float)sprite_w;
         positions[i].h = (float)sprite_h;
         velocities[i].x = 0.0f;
         velocities[i].y = 0.0f;
         while (velocities[i].x == 0.f && velocities[i].y == 0.f) {
-            velocities[i].x = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED);
-            velocities[i].y = (float)(SDL_rand_n(MAX_SPEED * 2 + 1) - MAX_SPEED);
+            velocities[i].x = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
+            velocities[i].y = (float)(SDL_rand(MAX_SPEED * 2 + 1) - MAX_SPEED);
         }
     }