random_seed: Rewrite the generic clock() based seed code
authorMichael Niedermayer <michaelni@gmx.at>
Mon, 11 Jun 2012 09:47:59 +0000 (11:47 +0200)
committerMartin Storsjö <martin@martin.st>
Mon, 6 Jan 2014 16:53:58 +0000 (18:53 +0200)
The new code is faster and reuses the previous state in case of
multiple calls.

The previous code could easily end up in near-infinite loops,
if the difference between two clock() calls never was larger than
1.

This makes fate-parseutils finish in finite time when run in wine,
if CryptGenRandom isn't available (which e.g. isn't available if
targeting Windows RT/metro).

Patch originally by Michael Niedermayer but with some modifications
by Martin Storsjö.

Signed-off-by: Martin Storsjö <martin@martin.st>
libavutil/random_seed.c

index 4680081..26884cb 100644 (file)
 #include <math.h>
 #include <time.h>
 #include "internal.h"
+#include "intreadwrite.h"
+#include "mem.h"
 #include "timer.h"
 #include "random_seed.h"
+#include "sha.h"
 
 static int read_random(uint32_t *dst, const char *file)
 {
@@ -53,34 +56,40 @@ static int read_random(uint32_t *dst, const char *file)
 
 static uint32_t get_generic_seed(void)
 {
+    struct AVSHA *sha = av_sha_alloc();
     clock_t last_t  = 0;
-    int bits        = 0;
-    uint64_t random = 0;
-    unsigned i;
-    float s = 0.000000000001;
+    static uint64_t i = 0;
+    static uint32_t buffer[512] = { 0 };
+    unsigned char digest[20];
+    uint64_t last_i = i;
 
-    for (i = 0; bits < 64; i++) {
+    for (;;) {
         clock_t t = clock();
-        if (last_t && fabs(t - last_t) > s || t == (clock_t) -1) {
-            if (i < 10000 && s < (1 << 24)) {
-                s += s;
-                i = t = 0;
-            } else {
-                random = 2 * random + (i & 1);
-                bits++;
-            }
+
+        if (last_t == t) {
+            buffer[i & 511]++;
+        } else {
+            buffer[++i & 511] += (t - last_t) % 3294638521U;
+            if (last_i && i - last_i > 4 || i - last_i > 64)
+                break;
         }
         last_t = t;
     }
-#ifdef AV_READ_TIME
-    random ^= AV_READ_TIME();
-#else
-    random ^= clock();
-#endif
 
-    random += random >> 32;
-
-    return random;
+    if (!sha) {
+        uint32_t seed = 0;
+        int j;
+        // Unable to allocate an sha context, just xor the buffer together
+        // to create something hopefully unique.
+        for (j = 0; j < 512; j++)
+            seed ^= buffer[j];
+        return seed;
+    }
+    av_sha_init(sha, 160);
+    av_sha_update(sha, (const uint8_t *) buffer, sizeof(buffer));
+    av_sha_final(sha, digest);
+    av_free(sha);
+    return AV_RB32(digest) + AV_RB32(digest + 16);
 }
 
 uint32_t av_get_random_seed(void)