Coverage Report

Created: 2025-12-17 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/brpc/src/butil/fast_rand.cpp
Line
Count
Source
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
18
// Date: Thu Dec 31 13:35:39 CST 2015
19
20
#include <limits>          // numeric_limits
21
#include <math.h>
22
#include "butil/basictypes.h"
23
#include "butil/macros.h"
24
#include "butil/time.h"     // gettimeofday_us()
25
#include "butil/fast_rand.h"
26
#include "butil/numerics/safe_conversions.h" // safe_abs
27
28
namespace butil {
29
30
// This state can be seeded with any value.
31
typedef uint64_t SplitMix64Seed;
32
33
// A very fast generator passing BigCrush. Due to its 64-bit state, it's
34
// solely for seeding xorshift128+.
35
0
inline uint64_t splitmix64_next(SplitMix64Seed* seed) {
36
0
    uint64_t z = (*seed += UINT64_C(0x9E3779B97F4A7C15));
37
0
    z = (z ^ (z >> 30)) * UINT64_C(0xBF58476D1CE4E5B9);
38
0
    z = (z ^ (z >> 27)) * UINT64_C(0x94D049BB133111EB);
39
0
    return z ^ (z >> 31);
40
0
}
41
42
// xorshift128+ is the fastest generator passing BigCrush without systematic
43
// failures
44
0
inline uint64_t xorshift128_next(FastRandSeed* seed) {
45
0
    uint64_t s1 = seed->s[0];
46
0
    const uint64_t s0 = seed->s[1];
47
0
    seed->s[0] = s0;
48
0
    s1 ^= s1 << 23; // a
49
0
    seed->s[1] = s1 ^ s0 ^ (s1 >> 18) ^ (s0 >> 5); // b, c
50
0
    return seed->s[1] + s0;
51
0
}
52
53
// seed xorshift128+ with splitmix64.
54
0
void init_fast_rand_seed(FastRandSeed* seed) {
55
0
    SplitMix64Seed seed4seed = butil::gettimeofday_us();
56
0
    seed->s[0] = splitmix64_next(&seed4seed);
57
0
    seed->s[1] = splitmix64_next(&seed4seed);
58
0
}
59
60
0
inline uint64_t fast_rand_impl(uint64_t range, FastRandSeed* seed) {
61
    // Separating uint64_t values into following intervals:
62
    //   [0,range-1][range,range*2-1] ... [uint64_max/range*range,uint64_max]
63
    // If the generated 64-bit random value falls into any interval except the
64
    // last one, the probability of taking any value inside [0, range-1] is
65
    // same. If the value falls into last interval, we retry the process until
66
    // the value falls into other intervals. If min/max are limited to 32-bits,
67
    // the retrying is rare. The amortized retrying count at maximum is 1 when 
68
    // range equals 2^32. A corner case is that even if the range is power of
69
    // 2(e.g. min=0 max=65535) in which case the retrying can be avoided, we
70
    // still retry currently. The reason is just to keep the code simpler
71
    // and faster for most cases.
72
0
    const uint64_t div = std::numeric_limits<uint64_t>::max() / range;
73
0
    uint64_t result;
74
0
    do {
75
0
        result = xorshift128_next(seed) / div;
76
0
    } while (result >= range);
77
0
    return result;
78
0
}
79
80
// Seeds for different threads are stored separately in thread-local storage.
81
static __thread FastRandSeed _tls_seed = { { 0, 0 } };
82
83
// True if the seed is (probably) uninitialized. There's definitely false
84
// positive, but it's OK for us.
85
0
inline bool need_init(const FastRandSeed& seed) {
86
0
    return seed.s[0] == 0 && seed.s[1] == 0;
87
0
}
88
89
0
uint64_t fast_rand() {
90
0
    if (need_init(_tls_seed)) {
91
0
        init_fast_rand_seed(&_tls_seed);
92
0
    }
93
0
    return xorshift128_next(&_tls_seed);
94
0
}
95
96
0
uint64_t fast_rand(FastRandSeed* seed) {
97
0
    return xorshift128_next(seed);
98
0
}
99
100
0
uint64_t fast_rand_less_than(uint64_t range) {
101
0
    if (range == 0) {
102
0
        return 0;
103
0
    }
104
0
    if (need_init(_tls_seed)) {
105
0
        init_fast_rand_seed(&_tls_seed);
106
0
    }
107
0
    return fast_rand_impl(range, &_tls_seed);
108
0
}
109
110
0
int64_t fast_rand_in_64(int64_t min, int64_t max) {
111
0
    if (need_init(_tls_seed)) {
112
0
        init_fast_rand_seed(&_tls_seed);
113
0
    }
114
0
    if (BAIDU_UNLIKELY(min >= max)) {
115
0
        if (min == max) {
116
0
            return min;
117
0
        }
118
0
        std::swap(min, max);
119
0
    }
120
0
    uint64_t range;
121
0
    if (min >= 0) {
122
        // Always safe to do subtraction.
123
0
        range = (uint64_t)(max - min) + 1;
124
0
        return min + (int64_t)fast_rand_impl(range, &_tls_seed);
125
0
    }
126
127
0
    uint64_t abs_min = safe_abs(min);
128
0
    if (max >= 0) {
129
0
        range = abs_min + (uint64_t)(max) + 1;
130
0
    } else {
131
0
        range = abs_min - safe_abs(max) + 1;
132
0
    }
133
0
    if (range == 0) {
134
        // max = INT64_MAX, min = INT64_MIN
135
0
        return (int64_t)xorshift128_next(&_tls_seed);
136
0
    }
137
0
    uint64_t r = fast_rand_impl(range, &_tls_seed);
138
0
    return r >= abs_min ? (int64_t)(r - abs_min) : -((int64_t)(abs_min - r));
139
0
}
140
141
0
uint64_t fast_rand_in_u64(uint64_t min, uint64_t max) {
142
0
    if (need_init(_tls_seed)) {
143
0
        init_fast_rand_seed(&_tls_seed);
144
0
    }
145
0
    if (min >= max) {
146
0
        if (min == max) {
147
0
            return min;
148
0
        }
149
0
        const uint64_t tmp = min;
150
0
        min = max;
151
0
        max = tmp;
152
0
    }
153
0
    uint64_t range = max - min + 1;
154
0
    if (range == 0) {
155
        // max = UINT64_MAX, min = UINT64_MIN
156
0
        return xorshift128_next(&_tls_seed);
157
0
    }
158
0
    return min + fast_rand_impl(range, &_tls_seed);
159
0
}
160
161
0
inline double fast_rand_double(FastRandSeed* seed) {
162
    // Copied from rand_util.cc
163
0
    COMPILE_ASSERT(std::numeric_limits<double>::radix == 2, otherwise_use_scalbn);
164
0
    static const int kBits = std::numeric_limits<double>::digits;
165
0
    uint64_t random_bits = xorshift128_next(seed) & ((UINT64_C(1) << kBits) - 1);
166
0
    double result = ldexp(static_cast<double>(random_bits), -1 * kBits);
167
0
    return result;
168
0
}
169
170
0
double fast_rand_double() {
171
0
    if (need_init(_tls_seed)) {
172
0
        init_fast_rand_seed(&_tls_seed);
173
0
    }
174
0
    return fast_rand_double(&_tls_seed);
175
0
}
176
177
0
void fast_rand_bytes(void* output, size_t output_length) {
178
0
    const size_t n = output_length / 8;
179
0
    for (size_t i = 0; i < n; ++i) {
180
0
        static_cast<uint64_t*>(output)[i] = fast_rand();
181
0
    }
182
0
    const size_t m = output_length - n * 8;
183
0
    if (m) {
184
0
        uint8_t* p = static_cast<uint8_t*>(output) + n * 8;
185
0
        uint64_t r = fast_rand();
186
0
        for (size_t i = 0; i < m; ++i) {
187
0
            p[i] = (r & 0xFF);
188
0
            r = (r >> 8);
189
0
        }
190
0
    }
191
0
}
192
193
0
std::string fast_rand_printable(size_t length) {
194
0
    std::string result(length, 0);
195
0
    const size_t halflen = length/2;
196
0
    fast_rand_bytes(&result[0], halflen);
197
0
    for (size_t i = 0; i < halflen; ++i) {
198
0
        const uint8_t b = result[halflen - 1 - i];
199
0
        result[length - 1 - 2*i] = 'A' + (b & 0xF);
200
0
        result[length - 2 - 2*i] = 'A' + (b >> 4);
201
0
    }
202
0
    if (halflen * 2 != length) {
203
0
        result[0] = 'A' + (fast_rand() % 16);
204
0
    }
205
0
    return result;
206
0
}
207
208
}  // namespace butil