OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "chrome/common/metrics/entropy_provider.h" | |
6 | |
7 #include <algorithm> | |
8 #include <limits> | |
9 #include <vector> | |
10 | |
11 #include "base/logging.h" | |
12 #include "base/rand_util.h" | |
13 #include "base/sha1.h" | |
14 #include "base/sys_byteorder.h" | |
15 | |
16 namespace internal { | |
17 | |
18 SeededRandGenerator::SeededRandGenerator(uint32 seed) { | |
19 mersenne_twister_.init_genrand(seed); | |
20 } | |
21 | |
22 SeededRandGenerator::~SeededRandGenerator() { | |
23 } | |
24 | |
25 uint32 SeededRandGenerator::operator()(uint32 range) { | |
26 // Based on base::RandGenerator(). | |
27 DCHECK_GT(range, 0u); | |
28 | |
29 // We must discard random results above this number, as they would | |
30 // make the random generator non-uniform (consider e.g. if | |
31 // MAX_UINT64 was 7 and |range| was 5, then a result of 1 would be twice | |
32 // as likely as a result of 3 or 4). | |
33 uint32 max_acceptable_value = | |
34 (std::numeric_limits<uint32>::max() / range) * range - 1; | |
35 | |
36 uint32 value; | |
37 do { | |
38 value = mersenne_twister_.genrand_int32(); | |
39 } while (value > max_acceptable_value); | |
40 | |
41 return value % range; | |
42 } | |
43 | |
44 uint32 HashName(const std::string& name) { | |
45 // SHA-1 is designed to produce a uniformly random spread in its output space, | |
46 // even for nearly-identical inputs. | |
47 unsigned char sha1_hash[base::kSHA1Length]; | |
48 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(name.c_str()), | |
jar (doing other things)
2012/08/17 19:06:19
Can we get away with a static_cast here?
Alexei Svitkine (slow)
2012/08/20 15:57:40
Doesn't look like it. This is what the compiler sa
| |
49 name.size(), | |
50 sha1_hash); | |
51 | |
52 uint32 bits; | |
53 COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data); | |
54 memcpy(&bits, sha1_hash, sizeof(bits)); | |
55 | |
56 return base::ByteSwapToLE32(bits); | |
57 } | |
58 | |
59 } // namespace internal | |
60 | |
61 SHA1EntropyProvider::SHA1EntropyProvider(const std::string& entropy_source) | |
62 : entropy_source_(entropy_source) { | |
63 } | |
64 | |
65 SHA1EntropyProvider::~SHA1EntropyProvider() { | |
66 } | |
67 | |
68 double SHA1EntropyProvider::GetEntropyForTrial( | |
69 const std::string& trial_name) const { | |
70 // SHA-1 is designed to produce a uniformly random spread in its output space, | |
71 // even for nearly-identical inputs, so it helps massage whatever client_id | |
72 // and trial_name we get into something with a uniform distribution, which | |
73 // is desirable so that we don't skew any part of the 0-100% spectrum. | |
hfung
2012/08/17 17:35:40
Mention that it doesn't work as well when there's
Alexei Svitkine (slow)
2012/08/17 17:57:40
Updated the comment.
| |
74 std::string input(entropy_source_ + trial_name); | |
75 unsigned char sha1_hash[base::kSHA1Length]; | |
76 base::SHA1HashBytes(reinterpret_cast<const unsigned char*>(input.c_str()), | |
jar (doing other things)
2012/08/17 19:06:19
Would static_cast work here?
Alexei Svitkine (slow)
2012/08/20 15:57:40
Same as above.
| |
77 input.size(), | |
78 sha1_hash); | |
79 | |
80 uint64 bits; | |
81 COMPILE_ASSERT(sizeof(bits) < sizeof(sha1_hash), need_more_data); | |
82 memcpy(&bits, sha1_hash, sizeof(bits)); | |
83 bits = base::ByteSwapToLE64(bits); | |
84 | |
85 return base::BitsToOpenEndedUnitInterval(bits); | |
86 } | |
87 | |
88 PermutedEntropyProvider::PermutedEntropyProvider( | |
89 uint16 low_entropy_source, | |
90 size_t low_entropy_source_max) | |
91 : low_entropy_source_(low_entropy_source), | |
jar (doing other things)
2012/08/17 19:06:19
nit: indent ":" 4 spaces from the "P" in "Permuted
Alexei Svitkine (slow)
2012/08/20 15:57:40
Done.
| |
92 low_entropy_source_max_(low_entropy_source_max) { | |
93 DCHECK_LT(low_entropy_source, low_entropy_source_max); | |
94 DCHECK_LE(low_entropy_source_max, std::numeric_limits<uint16>::max()); | |
95 } | |
96 | |
97 PermutedEntropyProvider::~PermutedEntropyProvider() { | |
98 } | |
99 | |
100 double PermutedEntropyProvider::GetEntropyForTrial( | |
101 const std::string& trial_name) const { | |
102 std::vector<uint16> mapping(low_entropy_source_max_); | |
103 for (size_t i = 0; i < mapping.size(); ++i) | |
104 mapping[i] = static_cast<uint16>(i); | |
105 | |
106 internal::SeededRandGenerator generator(internal::HashName(trial_name)); | |
107 std::random_shuffle(mapping.begin(), mapping.end(), generator); | |
108 | |
109 return mapping[low_entropy_source_] / | |
110 static_cast<double>(low_entropy_source_max_); | |
111 } | |
OLD | NEW |