Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Implementation of non-cryptographic pseudo-random number |
3 | | * generator algorithm known as xoshiro. |
4 | | * |
5 | | * This file is part of mpv. |
6 | | * |
7 | | * mpv is free software; you can redistribute it and/or |
8 | | * modify it under the terms of the GNU Lesser General Public |
9 | | * License as published by the Free Software Foundation; either |
10 | | * version 2.1 of the License, or (at your option) any later version. |
11 | | * |
12 | | * mpv is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU Lesser General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU Lesser General Public |
18 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
19 | | */ |
20 | | |
21 | | #include <stdint.h> |
22 | | |
23 | | #include <libavutil/random_seed.h> |
24 | | |
25 | | #include "common/common.h" |
26 | | #include "misc/mp_assert.h" |
27 | | #include "osdep/timer.h" |
28 | | #include "random.h" |
29 | | |
30 | | |
31 | | static inline uint64_t rotl_u64(const uint64_t x, const int k) |
32 | 22.8k | { |
33 | 22.8k | return (x << k) | (x >> (64 - k)); |
34 | 22.8k | } |
35 | | |
36 | | static inline uint64_t splitmix64(uint64_t *const x) |
37 | 372 | { |
38 | 372 | uint64_t z = (*x += UINT64_C(0x9e3779b97f4a7c15)); |
39 | 372 | z = (z ^ (z >> 30)) * UINT64_C(0xbf58476d1ce4e5b9); |
40 | 372 | z = (z ^ (z >> 27)) * UINT64_C(0x94d049bb133111eb); |
41 | 372 | return z ^ (z >> 31); |
42 | 372 | } |
43 | | |
44 | | mp_rand_state mp_rand_seed(uint64_t seed) |
45 | 124 | { |
46 | 124 | mp_rand_state ret; |
47 | | |
48 | 124 | #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION |
49 | 124 | seed = 42; |
50 | 124 | #endif |
51 | | |
52 | 124 | if (seed == 0) { |
53 | 0 | if (av_random_bytes((void *)ret.v, sizeof(ret.v)) == 0) { |
54 | 0 | return ret; |
55 | 0 | } |
56 | 0 | seed = mp_raw_time_ns(); |
57 | 0 | seed ^= (uintptr_t)&mp_rand_seed; // ASLR, hopefully |
58 | 0 | seed += (uintptr_t)&ret; // stack position |
59 | 0 | } |
60 | | |
61 | 124 | ret.v[0] = seed; |
62 | 496 | for (int i = 1; i < 4; i++) |
63 | 372 | ret.v[i] = splitmix64(&seed); |
64 | 124 | return ret; |
65 | 124 | } |
66 | | |
67 | | uint64_t mp_rand_next(mp_rand_state *s) |
68 | 11.4k | { |
69 | 11.4k | uint64_t result, t; |
70 | 11.4k | uint64_t *state = s->v; |
71 | | |
72 | 11.4k | result = rotl_u64(state[1] * 5, 7) * 9; |
73 | 11.4k | t = state[1] << 17; |
74 | | |
75 | 11.4k | state[2] ^= state[0]; |
76 | 11.4k | state[3] ^= state[1]; |
77 | 11.4k | state[1] ^= state[2]; |
78 | 11.4k | state[0] ^= state[3]; |
79 | 11.4k | state[2] ^= t; |
80 | 11.4k | state[3] = rotl_u64(state[3], 45); |
81 | | |
82 | 11.4k | return result; |
83 | 11.4k | } |
84 | | |
85 | | double mp_rand_next_double(mp_rand_state *s) |
86 | 0 | { |
87 | 0 | return (mp_rand_next(s) >> 11) * 0x1.0p-53; |
88 | 0 | } |
89 | | |
90 | | // <https://web.archive.org/web/20250321082025/ |
91 | | // https://www.pcg-random.org/posts/bounded-rands.html#bitmask-with-rejection-unbiased-apples-method> |
92 | | uint32_t mp_rand_in_range32(mp_rand_state *s, uint32_t min, uint32_t max) |
93 | 8.30k | { |
94 | 8.30k | mp_assert(min < max); |
95 | 8.30k | uint32_t range = max - min; |
96 | 8.30k | uint32_t mask = mp_round_next_power_of_2(range) - 1; |
97 | 8.30k | uint32_t ret; |
98 | 11.4k | do { |
99 | 11.4k | ret = mp_rand_next(s) & mask; |
100 | 11.4k | } while (ret >= range); |
101 | 8.30k | return min + ret; |
102 | 8.30k | } |