OLD | NEW |
| (Empty) |
1 // Copyright 2016 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 <limits> | |
6 #include <utility> | |
7 | |
8 #include "mojo/public/cpp/environment/logging.h" | |
9 #include "mojo/services/media/common/cpp/ratio.h" | |
10 | |
11 namespace mojo { | |
12 namespace media { | |
13 | |
14 namespace { | |
15 | |
16 // Calculates the greatest common denominator (factor) of two values. | |
17 template <typename T> | |
18 T BinaryGcd(T a, T b) { | |
19 if (a == 0) { | |
20 return b; | |
21 } | |
22 | |
23 if (b == 0) { | |
24 return a; | |
25 } | |
26 | |
27 // Remove and count the common factors of 2. | |
28 uint8_t twos; | |
29 for (twos = 0; ((a | b) & 1) == 0; ++twos) { | |
30 a >>= 1; | |
31 b >>= 1; | |
32 } | |
33 | |
34 // Get rid of the non-common factors of 2 in a. a is non-zero, so this | |
35 // terminates. | |
36 while ((a & 1) == 0) { | |
37 a >>= 1; | |
38 } | |
39 | |
40 do { | |
41 // Get rid of the non-common factors of 2 in b. b is non-zero, so this | |
42 // terminates. | |
43 while ((b & 1) == 0) { | |
44 b >>= 1; | |
45 } | |
46 | |
47 // Apply the Euclid subtraction method. | |
48 if (a > b) { | |
49 std::swap(a, b); | |
50 } | |
51 | |
52 b = b - a; | |
53 } while (b != 0); | |
54 | |
55 // Multiply in the common factors of two. | |
56 return a << twos; | |
57 } | |
58 | |
59 // Reduces the ration of *numerator and *denominator. | |
60 template <typename T> | |
61 void ReduceRatio(T* numerator, T* denominator) { | |
62 MOJO_DCHECK(numerator != nullptr); | |
63 MOJO_DCHECK(denominator != nullptr); | |
64 MOJO_DCHECK(*denominator != 0); | |
65 | |
66 T gcd = BinaryGcd(*numerator, *denominator); | |
67 | |
68 if (gcd == 0) { | |
69 *denominator = 1; | |
70 return; | |
71 } | |
72 | |
73 if (gcd == 1) { | |
74 return; | |
75 } | |
76 | |
77 *numerator = *numerator / gcd; | |
78 *denominator = *denominator / gcd; | |
79 } | |
80 | |
81 template void ReduceRatio<uint64_t>(uint64_t* numerator, uint64_t* denominator); | |
82 template void ReduceRatio<uint32_t>(uint32_t* numerator, uint32_t* denominator); | |
83 | |
84 // Scales a uint64_t value by the ratio of two uint32_t values. If round_up is | |
85 // true, the result is rounded up rather than down. overflow is set to indicate | |
86 // overflow. | |
87 uint64_t ScaleUInt64(uint64_t value, | |
88 uint32_t numerator, | |
89 uint32_t denominator, | |
90 bool round_up, | |
91 bool* overflow) { | |
92 MOJO_DCHECK(denominator != 0u); | |
93 MOJO_DCHECK(overflow != nullptr); | |
94 | |
95 constexpr uint64_t kLow32Bits = 0xffffffffu; | |
96 constexpr uint64_t kHigh32Bits = kLow32Bits << 32u; | |
97 | |
98 // high and low are the product of the numerator and the high and low halves | |
99 // (respectively) of value. | |
100 uint64_t high = numerator * (value >> 32u); | |
101 uint64_t low = numerator * (value & kLow32Bits); | |
102 // Ignoring overflow and remainder, the result we want is: | |
103 // ((high << 32) + low) / denominator. | |
104 | |
105 // Move the high end of low into the low end of high. | |
106 high += low >> 32u; | |
107 low = low & kLow32Bits; | |
108 // Ignoring overflow and remainder, the result we want is still: | |
109 // ((high << 32) + low) / denominator. | |
110 | |
111 // When we divide high by denominator, there'll be a remainder. Make | |
112 // that the high end of low, which is currently all zeroes. | |
113 low |= (high % denominator) << 32u; | |
114 | |
115 // Determine if we need to round up when we're done: | |
116 round_up = round_up && (low % denominator) != 0; | |
117 | |
118 // Do the division. | |
119 high /= denominator; | |
120 low /= denominator; | |
121 | |
122 // If high's top 32 bits aren't all zero, we have overflow. | |
123 if (high & kHigh32Bits) { | |
124 *overflow = true; | |
125 return 0; | |
126 } | |
127 | |
128 uint64_t result = (high << 32u) | low; | |
129 if (round_up) { | |
130 if (result == std::numeric_limits<int64_t>::max()) { | |
131 *overflow = true; | |
132 return 0; | |
133 } | |
134 ++result; | |
135 } | |
136 | |
137 *overflow = false; | |
138 return result; | |
139 } | |
140 | |
141 } // namespace | |
142 | |
143 // static | |
144 void Ratio::Reduce(uint32_t* numerator, uint32_t* denominator) { | |
145 ReduceRatio(numerator, denominator); | |
146 } | |
147 | |
148 // static | |
149 void Ratio::Product(uint32_t a_numerator, | |
150 uint32_t a_denominator, | |
151 uint32_t b_numerator, | |
152 uint32_t b_denominator, | |
153 uint32_t* product_numerator, | |
154 uint32_t* product_denominator, | |
155 bool exact) { | |
156 MOJO_DCHECK(a_denominator != 0); | |
157 MOJO_DCHECK(b_denominator != 0); | |
158 MOJO_DCHECK(product_numerator != nullptr); | |
159 MOJO_DCHECK(product_denominator != nullptr); | |
160 | |
161 uint64_t numerator = static_cast<uint64_t>(a_numerator) * b_numerator; | |
162 uint64_t denominator = static_cast<uint64_t>(a_denominator) * b_denominator; | |
163 | |
164 ReduceRatio(&numerator, &denominator); | |
165 | |
166 if (numerator > std::numeric_limits<uint32_t>::max() || | |
167 denominator > std::numeric_limits<uint32_t>::max()) { | |
168 MOJO_DCHECK(!exact); | |
169 | |
170 do { | |
171 numerator >>= 1; | |
172 denominator >>= 1; | |
173 } while (numerator > std::numeric_limits<uint32_t>::max() || | |
174 denominator > std::numeric_limits<uint32_t>::max()); | |
175 | |
176 if (denominator == 0) { | |
177 // Product is larger than we can represent. Return the largest value we | |
178 // can represent. | |
179 *product_numerator = std::numeric_limits<uint32_t>::max(); | |
180 *product_denominator = 1; | |
181 return; | |
182 } | |
183 } | |
184 | |
185 *product_numerator = static_cast<uint32_t>(numerator); | |
186 *product_denominator = static_cast<uint32_t>(denominator); | |
187 } | |
188 | |
189 // static | |
190 int64_t Ratio::Scale(int64_t value, uint32_t numerator, uint32_t denominator) { | |
191 static constexpr uint64_t abs_of_min_int64 = | |
192 static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1; | |
193 | |
194 MOJO_DCHECK(denominator != 0u); | |
195 | |
196 bool overflow; | |
197 | |
198 uint64_t abs_result; | |
199 | |
200 if (value >= 0) { | |
201 abs_result = ScaleUInt64(static_cast<uint64_t>(value), numerator, | |
202 denominator, false, &overflow); | |
203 } else if (value == std::numeric_limits<int64_t>::min()) { | |
204 abs_result = ScaleUInt64(abs_of_min_int64, numerator, denominator, | |
205 true, &overflow); | |
206 } else { | |
207 abs_result = ScaleUInt64(static_cast<uint64_t>(-value), numerator, | |
208 denominator, true, &overflow); | |
209 } | |
210 | |
211 if (overflow) { | |
212 return Ratio::kOverflow; | |
213 } | |
214 | |
215 // Make sure we won't overflow when we cast to int64_t. | |
216 if (abs_result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) { | |
217 if (value < 0 && abs_result == abs_of_min_int64) { | |
218 return std::numeric_limits<int64_t>::min(); | |
219 } | |
220 return Ratio::kOverflow; | |
221 } | |
222 | |
223 return value >= 0 ? static_cast<int64_t>(abs_result) | |
224 : -static_cast<int64_t>(abs_result); | |
225 } | |
226 | |
227 } // namespace media | |
228 } // namespace mojo | |
OLD | NEW |