This patch combine three patch which is related to "--gcov" flag.
[platform/framework/web/chromium-efl.git] / gin / time_clamper.h
1 // Copyright 2023 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #ifndef GIN_TIME_CLAMPER_H_
6 #define GIN_TIME_CLAMPER_H_
7
8 #include <algorithm>
9
10 #include "base/rand_util.h"
11 #include "base/time/time.h"
12 #include "gin/gin_export.h"
13
14 namespace gin {
15
16 // This class adds some amount of jitter to time. That is, for every
17 // `kResolutionMicros` microseconds it calculates a threshold (using a hash)
18 // that once exceeded advances to the next threshold. This is done so that
19 // time jumps slightly and does not move smoothly.
20 //
21 // NOTE: the implementation assumes it's used for servicing calls from JS,
22 // which uses the unix-epoch at time 0.
23 // TODO(skyostil): Deduplicate this with the clamper in Blink.
24 class GIN_EXPORT TimeClamper {
25  public:
26   // Public for tests.
27   static const int64_t kResolutionMicros;
28
29   TimeClamper() : secret_(base::RandUint64()) {}
30   // This constructor should only be used in tests.
31   explicit TimeClamper(uint64_t secret) : secret_(secret) {}
32
33   TimeClamper(const TimeClamper&) = delete;
34   TimeClamper& operator=(const TimeClamper&) = delete;
35   ~TimeClamper() = default;
36
37   // Clamps a time to millisecond precision. The return value is in milliseconds
38   // relative to unix-epoch (which is what JS uses).
39   inline int64_t ClampToMillis(base::Time time) const {
40     // Adding jitter is non-trivial, only use it if necessary.
41     // ClampTimeResolution() adjusts the time to land on `kResolutionMicros`
42     // boundaries, and either uses the current `kResolutionMicros` boundary, or
43     // the next one. Because `kResolutionMicros` is smaller than 1ms, and this
44     // function returns millisecond accuracy, ClampTimeResolution() is only
45     // necessary when within `kResolutionMicros` of the next millisecond.
46     const int64_t now_micros =
47         (time - base::Time::UnixEpoch()).InMicroseconds();
48     const int64_t micros = now_micros % 1000;
49     // abs() is necessary for devices with times before unix-epoch (most likely
50     // configured incorrectly).
51     if (abs(micros) + kResolutionMicros < 1000) {
52       return now_micros / 1000;
53     }
54     return ClampTimeResolution(now_micros) / 1000;
55   }
56
57   // Clamps the time, giving microsecond precision. The return value is in
58   // milliseconds relative to unix-epoch (which is what JS uses).
59   inline double ClampToMillisHighResolution(base::Time now) const {
60     const int64_t clamped_time =
61         ClampTimeResolution((now - base::Time::UnixEpoch()).InMicroseconds());
62     return static_cast<double>(clamped_time) / 1000.0;
63   }
64
65  private:
66   inline int64_t ClampTimeResolution(int64_t time_micros) const {
67     if (time_micros < 0) {
68       return -ClampTimeResolutionPositiveValue(-time_micros);
69     }
70     return ClampTimeResolutionPositiveValue(time_micros);
71   }
72
73   inline int64_t ClampTimeResolutionPositiveValue(int64_t time_micros) const {
74     DCHECK_GE(time_micros, 0u);
75     // For each clamped time interval, compute a pseudorandom transition
76     // threshold. The reported time will either be the start of that interval or
77     // the next one depending on which side of the threshold |time_seconds| is.
78     const int64_t interval = time_micros / kResolutionMicros;
79     const int64_t clamped_time_micros = interval * kResolutionMicros;
80     const int64_t tick_threshold = ThresholdFor(clamped_time_micros);
81     if (time_micros - clamped_time_micros < tick_threshold) {
82       return clamped_time_micros;
83     }
84     return clamped_time_micros + kResolutionMicros;
85   }
86
87   inline int64_t ThresholdFor(int64_t clamped_time) const {
88     // Returns a random value between 0 and kResolutionMicros. The distribution
89     // is not necessarily equal, but for a random value it's good enough.
90     // Avoid floating-point math by rewriting:
91     //   (random_value * 1.0 / UINT64_MAX) * kResolutionMicros
92     // into:
93     //   random_value / (UINT64_MAX / kResolutionMicros)
94     // where we avoid integer overflow by dividing instead of multiplying.
95     const uint64_t random_value = MurmurHash3(clamped_time ^ secret_);
96     return std::min(static_cast<int64_t>(random_value /
97                                          (std::numeric_limits<uint64_t>::max() /
98                                           kResolutionMicros)),
99                     kResolutionMicros);
100   }
101
102   static inline uint64_t MurmurHash3(uint64_t value) {
103     value ^= value >> 33;
104     value *= uint64_t{0xFF51AFD7ED558CCD};
105     value ^= value >> 33;
106     value *= uint64_t{0xC4CEB9FE1A85EC53};
107     value ^= value >> 33;
108     return value;
109   }
110
111   const uint64_t secret_;
112 };
113
114 }  // namespace gin
115
116 #endif  // GIN_TIME_CLAMPER_H_