Motown: Add new Ratio and LinearFunction classes to media/common/cpp.
Ratio and LinearFunction duplicate LinearTransform::Ratio and
LinearTransform with some improvements in terminology and notational
convenience. Unit tests are included. These classes have been added to
mojo/services/media/common/cpp so that clients can use them. The new
timing code will use these classes.
R=kulakowski@chromium.org
Review URL: https://codereview.chromium.org/1950603002 .
diff --git a/mojo/services/media/common/cpp/BUILD.gn b/mojo/services/media/common/cpp/BUILD.gn
index 627a726..e46af00 100644
--- a/mojo/services/media/common/cpp/BUILD.gn
+++ b/mojo/services/media/common/cpp/BUILD.gn
@@ -12,11 +12,15 @@
"circular_buffer_media_pipe_adapter.h",
"fifo_allocator.cc",
"fifo_allocator.h",
+ "linear_function.cc",
+ "linear_function.h",
"linear_transform.cc",
"linear_transform.h",
"local_time.h",
"mapped_shared_buffer.cc",
"mapped_shared_buffer.h",
+ "ratio.cc",
+ "ratio.h",
"shared_media_buffer_allocator.cc",
"shared_media_buffer_allocator.h",
]
@@ -36,3 +40,5 @@
"$mojo_sdk_root/mojo/services/media/common/interfaces",
]
}
+# Look for unit tests in services/media/common/test
+# TODO(dalesat): Move unit tests here using different test infrastructure.
diff --git a/mojo/services/media/common/cpp/linear_function.cc b/mojo/services/media/common/cpp/linear_function.cc
new file mode 100644
index 0000000..ec662e7
--- /dev/null
+++ b/mojo/services/media/common/cpp/linear_function.cc
@@ -0,0 +1,31 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+#include <utility>
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/linear_function.h"
+
+namespace mojo {
+namespace media {
+
+// static
+int64_t LinearFunction::Apply(int64_t domain_basis,
+ int64_t range_basis,
+ const Ratio& slope, // range_delta / domain_delta
+ int64_t domain_input) {
+ return slope.Scale(domain_input - domain_basis) + range_basis;
+}
+
+// static
+LinearFunction LinearFunction::Compose(const LinearFunction& bc,
+ const LinearFunction& ab,
+ bool exact) {
+ return LinearFunction(ab.domain_basis(), bc.Apply(ab.range_basis()),
+ Ratio::Product(ab.slope(), bc.slope(), exact));
+}
+
+} // namespace media
+} // namespace mojo
diff --git a/mojo/services/media/common/cpp/linear_function.h b/mojo/services/media/common/cpp/linear_function.h
new file mode 100644
index 0000000..be66bb3
--- /dev/null
+++ b/mojo/services/media/common/cpp/linear_function.h
@@ -0,0 +1,123 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_FUNCTION_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_FUNCTION_H_
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/ratio.h"
+
+namespace mojo {
+namespace media {
+
+// TODO(dalesat): Consider always allowing inexact results.
+
+// A linear function from int64_t to int64_t with non-negative slope. The
+// representation is in point-slope form. The point is represented as two
+// int64_t values (domain_basis, range_basis), and the slope is represented as
+// the ratio of two uint32_t values (range_delta / domain_delta). 'Domain'
+// refers to the input space, and 'range' refers to the output space.
+struct LinearFunction {
+ // Applies a linear function.
+ static int64_t Apply(int64_t domain_basis,
+ int64_t range_basis,
+ const Ratio& slope, // range_delta / domain_delta
+ int64_t domain_input);
+
+ // Applies the inverse of a linear function.
+ static int64_t ApplyInverse(int64_t domain_basis,
+ int64_t range_basis,
+ const Ratio& slope, // range_delta / domain_delta
+ int64_t range_input) {
+ MOJO_DCHECK(slope.denominator() != 0u);
+ return Apply(range_basis, domain_basis, slope.Inverse(), range_input);
+ }
+
+ // Composes two linear functions B->C and A->B producing A->C. If exact is
+ // true, DCHECKs on loss of precision.
+ static LinearFunction Compose(const LinearFunction& bc,
+ const LinearFunction& ab,
+ bool exact = true);
+
+ LinearFunction() : domain_basis_(0), range_basis_(0) {}
+
+ LinearFunction(int64_t domain_basis,
+ int64_t range_basis,
+ uint32_t domain_delta,
+ uint32_t range_delta)
+ : domain_basis_(domain_basis),
+ range_basis_(range_basis),
+ slope_(range_delta, domain_delta) {}
+
+ LinearFunction(int64_t domain_basis,
+ int64_t range_basis,
+ const Ratio& slope) // range_delta / domain_delta
+ : domain_basis_(domain_basis),
+ range_basis_(range_basis),
+ slope_(slope) {}
+
+ explicit LinearFunction(const Ratio& slope) // range_delta / domain_delta
+ : domain_basis_(0),
+ range_basis_(0),
+ slope_(slope) {}
+
+ // Applies the function. Returns Ratio::kOverflow on overflow.
+ int64_t Apply(int64_t domain_input) const {
+ return Apply(domain_basis_, range_basis_, slope_, domain_input);
+ }
+
+ // Applies the inverse of the function. Returns Ratio::kOverflow on overflow.
+ int64_t ApplyInverse(int64_t range_input) const {
+ MOJO_DCHECK(slope_.denominator() != 0u);
+ return ApplyInverse(domain_basis_, range_basis_, slope_, range_input);
+ }
+
+ // Applies the function. Returns Ratio::kOverflow on overflow.
+ int64_t operator()(int64_t domain_input) const { return Apply(domain_input); }
+
+ // Returns a linear function that is the inverse if this linear function.
+ LinearFunction Inverse() const {
+ MOJO_DCHECK(slope_.denominator() != 0u);
+ return LinearFunction(range_basis_, domain_basis_, slope_.Inverse());
+ }
+
+ int64_t domain_basis() const { return domain_basis_; }
+
+ int64_t range_basis() const { return range_basis_; }
+
+ const Ratio& slope() const { return slope_; }
+
+ uint32_t domain_delta() const { return slope_.denominator(); }
+
+ uint32_t range_delta() const { return slope_.numerator(); }
+
+ int64_t domain_basis_;
+ int64_t range_basis_;
+ Ratio slope_; // range_delta / domain_delta
+};
+
+// Tests two linear functions for equality. Equality requires equal basis
+// values.
+inline bool operator==(const LinearFunction& a, const LinearFunction& b) {
+ return a.domain_basis() == b.domain_basis() &&
+ a.range_basis() == b.range_basis() && a.slope() == b.slope();
+}
+
+// Tests two linear functions for inequality. Equality requires equal basis
+// values.
+inline bool operator!=(const LinearFunction& a, const LinearFunction& b) {
+ return !(a == b);
+}
+
+// Composes two linear functions B->C and A->B producing A->C. DCHECKs on
+// loss of precision.
+inline LinearFunction operator*(const LinearFunction& bc,
+ const LinearFunction& ab) {
+ return LinearFunction::Compose(bc, ab);
+}
+
+} // namespace media
+} // namespace mojo
+
+#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_FUNCTION_H_
diff --git a/mojo/services/media/common/cpp/ratio.cc b/mojo/services/media/common/cpp/ratio.cc
new file mode 100644
index 0000000..56a3f51
--- /dev/null
+++ b/mojo/services/media/common/cpp/ratio.cc
@@ -0,0 +1,228 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+#include <utility>
+
+#include "mojo/public/cpp/environment/logging.h"
+#include "mojo/services/media/common/cpp/ratio.h"
+
+namespace mojo {
+namespace media {
+
+namespace {
+
+// Calculates the greatest common denominator (factor) of two values.
+template <typename T>
+T BinaryGcd(T a, T b) {
+ if (a == 0) {
+ return b;
+ }
+
+ if (b == 0) {
+ return a;
+ }
+
+ // Remove and count the common factors of 2.
+ uint8_t twos;
+ for (twos = 0; ((a | b) & 1) == 0; ++twos) {
+ a >>= 1;
+ b >>= 1;
+ }
+
+ // Get rid of the non-common factors of 2 in a. a is non-zero, so this
+ // terminates.
+ while ((a & 1) == 0) {
+ a >>= 1;
+ }
+
+ do {
+ // Get rid of the non-common factors of 2 in b. b is non-zero, so this
+ // terminates.
+ while ((b & 1) == 0) {
+ b >>= 1;
+ }
+
+ // Apply the Euclid subtraction method.
+ if (a > b) {
+ std::swap(a, b);
+ }
+
+ b = b - a;
+ } while (b != 0);
+
+ // Multiply in the common factors of two.
+ return a << twos;
+}
+
+// Reduces the ration of *numerator and *denominator.
+template <typename T>
+void ReduceRatio(T* numerator, T* denominator) {
+ MOJO_DCHECK(numerator != nullptr);
+ MOJO_DCHECK(denominator != nullptr);
+ MOJO_DCHECK(*denominator != 0);
+
+ T gcd = BinaryGcd(*numerator, *denominator);
+
+ if (gcd == 0) {
+ *denominator = 1;
+ return;
+ }
+
+ if (gcd == 1) {
+ return;
+ }
+
+ *numerator = *numerator / gcd;
+ *denominator = *denominator / gcd;
+}
+
+template void ReduceRatio<uint64_t>(uint64_t* numerator, uint64_t* denominator);
+template void ReduceRatio<uint32_t>(uint32_t* numerator, uint32_t* denominator);
+
+// Scales a uint64_t value by the ratio of two uint32_t values. If round_up is
+// true, the result is rounded up rather than down. overflow is set to indicate
+// overflow.
+uint64_t ScaleUInt64(uint64_t value,
+ uint32_t numerator,
+ uint32_t denominator,
+ bool round_up,
+ bool* overflow) {
+ MOJO_DCHECK(denominator != 0u);
+ MOJO_DCHECK(overflow != nullptr);
+
+ constexpr uint64_t kLow32Bits = 0xffffffffu;
+ constexpr uint64_t kHigh32Bits = kLow32Bits << 32u;
+
+ // high and low are the product of the numerator and the high and low halves
+ // (respectively) of value.
+ uint64_t high = numerator * (value >> 32u);
+ uint64_t low = numerator * (value & kLow32Bits);
+ // Ignoring overflow and remainder, the result we want is:
+ // ((high << 32) + low) / denominator.
+
+ // Move the high end of low into the low end of high.
+ high += low >> 32u;
+ low = low & kLow32Bits;
+ // Ignoring overflow and remainder, the result we want is still:
+ // ((high << 32) + low) / denominator.
+
+ // When we divide high by denominator, there'll be a remainder. Make
+ // that the high end of low, which is currently all zeroes.
+ low |= (high % denominator) << 32u;
+
+ // Determine if we need to round up when we're done:
+ round_up = round_up && (low % denominator) != 0;
+
+ // Do the division.
+ high /= denominator;
+ low /= denominator;
+
+ // If high's top 32 bits aren't all zero, we have overflow.
+ if (high & kHigh32Bits) {
+ *overflow = true;
+ return 0;
+ }
+
+ uint64_t result = (high << 32u) | low;
+ if (round_up) {
+ if (result == std::numeric_limits<int64_t>::max()) {
+ *overflow = true;
+ return 0;
+ }
+ ++result;
+ }
+
+ *overflow = false;
+ return result;
+}
+
+} // namespace
+
+// static
+void Ratio::Reduce(uint32_t* numerator, uint32_t* denominator) {
+ ReduceRatio(numerator, denominator);
+}
+
+// static
+void Ratio::Product(uint32_t a_numerator,
+ uint32_t a_denominator,
+ uint32_t b_numerator,
+ uint32_t b_denominator,
+ uint32_t* product_numerator,
+ uint32_t* product_denominator,
+ bool exact) {
+ MOJO_DCHECK(a_denominator != 0);
+ MOJO_DCHECK(b_denominator != 0);
+ MOJO_DCHECK(product_numerator != nullptr);
+ MOJO_DCHECK(product_denominator != nullptr);
+
+ uint64_t numerator = static_cast<uint64_t>(a_numerator) * b_numerator;
+ uint64_t denominator = static_cast<uint64_t>(a_denominator) * b_denominator;
+
+ ReduceRatio(&numerator, &denominator);
+
+ if (numerator > std::numeric_limits<uint32_t>::max() ||
+ denominator > std::numeric_limits<uint32_t>::max()) {
+ MOJO_DCHECK(!exact);
+
+ do {
+ numerator >>= 1;
+ denominator >>= 1;
+ } while (numerator > std::numeric_limits<uint32_t>::max() ||
+ denominator > std::numeric_limits<uint32_t>::max());
+
+ if (denominator == 0) {
+ // Product is larger than we can represent. Return the largest value we
+ // can represent.
+ *product_numerator = std::numeric_limits<uint32_t>::max();
+ *product_denominator = 1;
+ return;
+ }
+ }
+
+ *product_numerator = static_cast<uint32_t>(numerator);
+ *product_denominator = static_cast<uint32_t>(denominator);
+}
+
+// static
+int64_t Ratio::Scale(int64_t value, uint32_t numerator, uint32_t denominator) {
+ static constexpr uint64_t abs_of_min_int64 =
+ static_cast<uint64_t>(std::numeric_limits<int64_t>::max()) + 1;
+
+ MOJO_DCHECK(denominator != 0u);
+
+ bool overflow;
+
+ uint64_t abs_result;
+
+ if (value >= 0) {
+ abs_result = ScaleUInt64(static_cast<uint64_t>(value), numerator,
+ denominator, false, &overflow);
+ } else if (value == std::numeric_limits<int64_t>::min()) {
+ abs_result = ScaleUInt64(abs_of_min_int64, numerator, denominator,
+ true, &overflow);
+ } else {
+ abs_result = ScaleUInt64(static_cast<uint64_t>(-value), numerator,
+ denominator, true, &overflow);
+ }
+
+ if (overflow) {
+ return Ratio::kOverflow;
+ }
+
+ // Make sure we won't overflow when we cast to int64_t.
+ if (abs_result > static_cast<uint64_t>(std::numeric_limits<int64_t>::max())) {
+ if (value < 0 && abs_result == abs_of_min_int64) {
+ return std::numeric_limits<int64_t>::min();
+ }
+ return Ratio::kOverflow;
+ }
+
+ return value >= 0 ? static_cast<int64_t>(abs_result)
+ : -static_cast<int64_t>(abs_result);
+}
+
+} // namespace media
+} // namespace mojo
diff --git a/mojo/services/media/common/cpp/ratio.h b/mojo/services/media/common/cpp/ratio.h
new file mode 100644
index 0000000..0755c6e
--- /dev/null
+++ b/mojo/services/media/common/cpp/ratio.h
@@ -0,0 +1,118 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_RATIO_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_RATIO_H_
+
+#include <stdint.h>
+
+#include <limits>
+
+#include "mojo/public/cpp/environment/logging.h"
+
+namespace mojo {
+namespace media {
+
+// TODO(dalesat): Consider always allowing inexact results.
+
+// Expresses a non-negative rational number as the ratio between two uint32_t
+// values.
+struct Ratio {
+ // Used to indicate overflow of scaling operations.
+ static constexpr int64_t kOverflow = std::numeric_limits<int64_t>::max();
+
+ // Reduces the ratio of *numerator and *denominator.
+ static void Reduce(uint32_t* numerator, uint32_t* denominator);
+
+ // Produces the product of the ratios. If exact is true, DCHECKs on loss of
+ // precision.
+ static void Product(uint32_t a_numerator,
+ uint32_t a_denominator,
+ uint32_t b_numerator,
+ uint32_t b_denominator,
+ uint32_t* product_numerator,
+ uint32_t* product_denominator,
+ bool exact = true);
+
+ // Produces the product of the ratios and the int64_t as an int64_t. Returns
+ // kOverflow on overflow.
+ static int64_t Scale(int64_t value, uint32_t numerator, uint32_t denominator);
+
+ // Returns the product of the ratios. If exact is true, DCHECKs on loss of
+ // precision.
+ static Ratio Product(const Ratio& a, const Ratio& b, bool exact = true) {
+ uint32_t result_numerator;
+ uint32_t result_denominator;
+ Product(a.numerator(), a.denominator(), b.numerator(), b.denominator(),
+ &result_numerator, &result_denominator, exact);
+ return Ratio(result_numerator, result_denominator);
+ }
+
+ Ratio() : numerator_(0), denominator_(1) {}
+
+ explicit Ratio(uint32_t numerator) : numerator_(numerator), denominator_(1) {}
+
+ Ratio(uint32_t numerator, uint32_t denominator)
+ : numerator_(numerator), denominator_(denominator) {
+ MOJO_DCHECK(denominator != 0);
+ Reduce(&numerator_, &denominator_);
+ }
+
+ // Returns the inverse of the ratio. DCHECKs if the numerator of this ratio
+ // is zero.
+ Ratio Inverse() const {
+ MOJO_DCHECK(numerator_ != 0);
+ return Ratio(denominator_, numerator_);
+ }
+
+ // Scales the value by this ratio. Returns kOverflow on overflow.
+ int64_t Scale(int64_t value) const {
+ return Scale(value, numerator_, denominator_);
+ }
+
+ uint32_t numerator() const { return numerator_; }
+ uint32_t denominator() const { return denominator_; }
+
+ private:
+ uint32_t numerator_;
+ uint32_t denominator_;
+};
+
+// Tests two ratios for equality.
+inline bool operator==(const Ratio& a, const Ratio& b) {
+ return a.numerator() == b.numerator() && a.denominator() == b.denominator();
+}
+
+// Tests two ratios for inequality.
+inline bool operator!=(const Ratio& a, const Ratio& b) {
+ return !(a == b);
+}
+
+// Returns the product of the two ratios. DCHECKs on loss of precision.
+inline Ratio operator*(const Ratio& a, const Ratio& b) {
+ return Ratio::Product(a, b);
+}
+
+// Returns the product of the ratio and the int64_t. Returns kOverflow on
+// overflow.
+inline int64_t operator*(const Ratio& a, int64_t b) {
+ return a.Scale(b);
+}
+
+// Returns the product of the ratio and the int64_t. Returns kOverflow on
+// overflow.
+inline int64_t operator*(int64_t a, const Ratio& b) {
+ return b.Scale(a);
+}
+
+// Returns the the int64_t divided by the ratio. Returns kOverflow on
+// overflow.
+inline int64_t operator/(int64_t a, const Ratio& b) {
+ return b.Inverse().Scale(a);
+}
+
+} // namespace media
+} // namespace mojo
+
+#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_LINEAR_TRANSFORM_H_
diff --git a/services/BUILD.gn b/services/BUILD.gn
index 8254c5e..6d4e00b 100644
--- a/services/BUILD.gn
+++ b/services/BUILD.gn
@@ -82,6 +82,7 @@
"//services/clipboard:apptests",
"//services/gfx/compositor:apptests",
"//services/http_server:apptests",
+ "//services/media/common:apptests",
"//services/native_support:apptests",
"//services/prediction:apptests",
]
diff --git a/services/media/common/BUILD.gn b/services/media/common/BUILD.gn
index fb22148..dc8e8b6 100644
--- a/services/media/common/BUILD.gn
+++ b/services/media/common/BUILD.gn
@@ -3,6 +3,7 @@
# found in the LICENSE file.
import("//build/module_args/mojo.gni")
+import("//mojo/public/mojo_application.gni")
import("$mojo_sdk_root/mojo/public/mojo_sdk.gni")
source_set("common") {
@@ -18,3 +19,22 @@
"//mojo/services/media/common/interfaces",
]
}
+
+mojo_native_application("apptests") {
+ output_name = "media_common_apptests"
+
+ testonly = true
+
+ sources = [
+ "test/linear_function_test.cc",
+ "test/ratio_test.cc",
+ "test/test_base.h",
+ ]
+
+ deps = [
+ "//base",
+ "//mojo/application",
+ "//mojo/application:test_support",
+ "//mojo/services/media/common/cpp",
+ ]
+}
diff --git a/services/media/common/test/linear_function_test.cc b/services/media/common/test/linear_function_test.cc
new file mode 100644
index 0000000..b2ab53d
--- /dev/null
+++ b/services/media/common/test/linear_function_test.cc
@@ -0,0 +1,226 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "mojo/services/media/common/cpp/linear_function.h"
+#include "services/media/common/test/test_base.h"
+
+namespace mojo {
+namespace media {
+namespace {
+
+class LinearFunctionTest : public TestBase {
+ public:
+ // Verifies that a LinearFunction instantiated in three different ways with
+ // the given arguments has the expected properties.
+ void VerifyBasics(int64_t domain_basis,
+ int64_t range_basis,
+ uint32_t domain_delta,
+ uint32_t range_delta) {
+ LinearFunction under_test_1(domain_basis, range_basis, domain_delta,
+ range_delta);
+ VerifyBasics(under_test_1, domain_basis, range_basis, domain_delta,
+ range_delta);
+
+ LinearFunction under_test_2(domain_basis, range_basis,
+ Ratio(range_delta, domain_delta));
+ VerifyBasics(under_test_2, domain_basis, range_basis, domain_delta,
+ range_delta);
+
+ LinearFunction under_test_3(Ratio(range_delta, domain_delta));
+ VerifyBasics(under_test_3, 0, 0, domain_delta, range_delta);
+
+ EXPECT_EQ(under_test_1, under_test_1);
+ EXPECT_EQ(under_test_1, under_test_2);
+ EXPECT_EQ(under_test_2, under_test_1);
+ EXPECT_EQ(under_test_2, under_test_2);
+
+ if (domain_basis == 0 && range_basis == 0) {
+ EXPECT_EQ(under_test_1, under_test_3);
+ EXPECT_EQ(under_test_2, under_test_3);
+ EXPECT_EQ(under_test_3, under_test_1);
+ EXPECT_EQ(under_test_3, under_test_2);
+ } else {
+ EXPECT_NE(under_test_1, under_test_3);
+ EXPECT_NE(under_test_2, under_test_3);
+ EXPECT_NE(under_test_3, under_test_1);
+ EXPECT_NE(under_test_3, under_test_2);
+ }
+ }
+
+ // Verifies that the given LinearFunction instantiated with the given
+ // arguments has the expected properties.
+ void VerifyBasics(const LinearFunction& under_test,
+ int64_t domain_basis,
+ int64_t range_basis,
+ uint32_t domain_delta,
+ uint32_t range_delta) {
+ Ratio::Reduce(&range_delta, &domain_delta);
+ EXPECT_EQ(domain_basis, under_test.domain_basis());
+ EXPECT_EQ(range_basis, under_test.range_basis());
+ EXPECT_EQ(domain_delta, under_test.domain_delta());
+ EXPECT_EQ(range_delta, under_test.range_delta());
+ EXPECT_EQ(domain_delta, under_test.slope().denominator());
+ EXPECT_EQ(range_delta, under_test.slope().numerator());
+ }
+
+ // Verifies that the inverse of a LinearFunction instantiated in three
+ // different ways with the given arguments has the expected properties.
+ void VerifyInverse(int64_t domain_basis,
+ int64_t range_basis,
+ uint32_t domain_delta,
+ uint32_t range_delta) {
+ LinearFunction under_test_1(domain_basis, range_basis, domain_delta,
+ range_delta);
+ VerifyBasics(under_test_1.Inverse(), range_basis, domain_basis, range_delta,
+ domain_delta);
+
+ LinearFunction under_test_2(domain_basis, range_basis,
+ Ratio(range_delta, domain_delta));
+ VerifyBasics(under_test_2.Inverse(), range_basis, domain_basis, range_delta,
+ domain_delta);
+
+ LinearFunction under_test_3(Ratio(range_delta, domain_delta));
+ VerifyBasics(under_test_3.Inverse(), 0, 0, range_delta, domain_delta);
+ }
+
+ // Verifies that LinearFunction::Apply, in its various forms, works as
+ // expected for the given arguments.
+ void VerifyApply(int64_t domain_basis,
+ int64_t range_basis,
+ uint32_t domain_delta,
+ uint32_t range_delta,
+ int64_t domain_input,
+ int64_t expected_result) {
+ // Verify the static method.
+ EXPECT_EQ(
+ expected_result,
+ LinearFunction::Apply(domain_basis, range_basis,
+ Ratio(range_delta, domain_delta), domain_input));
+
+ // Verify the instance method.
+ LinearFunction under_test(domain_basis, range_basis, domain_delta,
+ range_delta);
+ EXPECT_EQ(expected_result, under_test.Apply(domain_input));
+
+ // Verify the operator.
+ EXPECT_EQ(expected_result, under_test(domain_input));
+ }
+
+ // Verifies that LinearFunction::ApplyInverse, in its various forms, works as
+ // expected for the given arguments.
+ void VerifyApplyInverse(int64_t domain_basis,
+ int64_t range_basis,
+ uint32_t domain_delta,
+ uint32_t range_delta,
+ int64_t range_input,
+ int64_t expected_result) {
+ // Verify the static method.
+ EXPECT_EQ(expected_result,
+ LinearFunction::ApplyInverse(domain_basis, range_basis,
+ Ratio(range_delta, domain_delta),
+ range_input));
+
+ // Verify the instance method.
+ LinearFunction under_test(domain_basis, range_basis, domain_delta,
+ range_delta);
+ EXPECT_EQ(expected_result, under_test.ApplyInverse(range_input));
+ }
+
+ // Verifies that LinearFunction::Compose works as expected with the given
+ // inputs.
+ void VerifyCompose(const LinearFunction& a,
+ const LinearFunction& b,
+ bool exact,
+ const LinearFunction& expected_result) {
+ // Verify the static method.
+ EXPECT_EQ(expected_result, LinearFunction::Compose(a, b, exact));
+ }
+};
+
+// Tests LinearFunction basics for various instantiation arguments.
+TEST_F(LinearFunctionTest, Basics) {
+ VerifyBasics(0, 0, 1, 0);
+ VerifyBasics(0, 0, 1, 1);
+ VerifyBasics(1, 1, 10, 10);
+ VerifyBasics(1234, 5678, 4321, 8765);
+ VerifyBasics(-1234, 5678, 4321, 8765);
+ VerifyBasics(-1234, -5678, 4321, 8765);
+ VerifyBasics(1234, -5678, 4321, 8765);
+}
+
+// Tests LinearFunction::Inverse.
+TEST_F(LinearFunctionTest, Inverse) {
+ VerifyInverse(0, 0, 1, 1);
+ VerifyInverse(1, 1, 10, 10);
+ VerifyInverse(1234, 5678, 4321, 8765);
+ VerifyInverse(-1234, 5678, 4321, 8765);
+ VerifyInverse(-1234, -5678, 4321, 8765);
+ VerifyInverse(1234, -5678, 4321, 8765);
+}
+
+// Tests LinearFunction::Apply in its variations.
+TEST_F(LinearFunctionTest, Apply) {
+ VerifyApply(0, 0, 1, 0, 0, 0);
+ VerifyApply(0, 0, 1, 0, 1000, 0);
+ VerifyApply(0, 1234, 1, 0, 0, 1234);
+ VerifyApply(0, 1234, 1, 0, 1000, 1234);
+ VerifyApply(0, 1234, 1, 0, -1000, 1234);
+ VerifyApply(0, -1234, 1, 0, 0, -1234);
+ VerifyApply(0, -1234, 1, 0, 1000, -1234);
+ VerifyApply(0, -1234, 1, 0, -1000, -1234);
+ VerifyApply(0, 0, 1, 1, 0, 0);
+ VerifyApply(0, 0, 1, 1, 1000, 1000);
+ VerifyApply(0, 1234, 1, 1, 0, 1234);
+ VerifyApply(0, 1234, 1, 1, 1000, 2234);
+ VerifyApply(0, 1234, 1, 1, -1000, 234);
+ VerifyApply(0, -1234, 1, 1, 0, -1234);
+ VerifyApply(0, -1234, 1, 1, 1000, -234);
+ VerifyApply(0, -1234, 1, 1, -1000, -2234);
+ VerifyApply(10, 0, 1, 0, 0, 0);
+ VerifyApply(10, 0, 1, 1, 0, -10);
+ VerifyApply(-10, 0, 1, 0, 0, 0);
+ VerifyApply(-10, 0, 1, 1, 0, 10);
+ VerifyApply(0, 1234, 2, 1, 0, 1234);
+ VerifyApply(0, 1234, 2, 1, 1234, 1234 + 1234 / 2);
+ VerifyApply(0, 1234, 1, 2, 1234, 1234 + 1234 * 2);
+}
+
+// Tests LinearFunction::Apply in its variations.
+TEST_F(LinearFunctionTest, ApplyInverse) {
+ VerifyApplyInverse(0, 0, 1, 1, 0, 0);
+ VerifyApplyInverse(0, 0, 1, 1, 1000, 1000);
+ VerifyApplyInverse(0, 1234, 1, 1, 1234, 0);
+ VerifyApplyInverse(0, 1234, 1, 1, 2234, 1000);
+ VerifyApplyInverse(0, 1234, 1, 1, 234, -1000);
+ VerifyApplyInverse(0, -1234, 1, 1, -1234, 0);
+ VerifyApplyInverse(0, -1234, 1, 1, -234, 1000);
+ VerifyApplyInverse(0, -1234, 1, 1, -2234, -1000);
+ VerifyApplyInverse(10, 0, 1, 1, -10, 0);
+ VerifyApplyInverse(-10, 0, 1, 1, 10, 0);
+ VerifyApplyInverse(0, 1234, 2, 1, 1234, 0);
+ VerifyApplyInverse(0, 1234, 2, 1, 1234 + 1234 / 2, 1234);
+ VerifyApplyInverse(0, 1234, 1, 2, 1234 + 1234 * 2, 1234);
+}
+
+// Tests LinearFunction::Compose.
+TEST_F(LinearFunctionTest, Compose) {
+ VerifyCompose(LinearFunction(0, 0, 1, 0), LinearFunction(0, 0, 1, 0), true,
+ LinearFunction(0, 0, 1, 0));
+ VerifyCompose(LinearFunction(0, 0, 1, 1), LinearFunction(0, 0, 1, 1), true,
+ LinearFunction(0, 0, 1, 1));
+ VerifyCompose(LinearFunction(1, 0, 1, 1), LinearFunction(0, 0, 1, 1), true,
+ LinearFunction(0, -1, 1, 1));
+ VerifyCompose(LinearFunction(10, 10, 1, 1), LinearFunction(0, 0, 1, 1), true,
+ LinearFunction(0, 0, 1, 1));
+ VerifyCompose(LinearFunction(0, 0, 1, 2), LinearFunction(0, 0, 1, 2), true,
+ LinearFunction(0, 0, 1, 4));
+ VerifyCompose(LinearFunction(0, 0, 2, 1), LinearFunction(0, 0, 2, 1), true,
+ LinearFunction(0, 0, 4, 1));
+ VerifyCompose(LinearFunction(0, 0, 2, 1), LinearFunction(0, 0, 1, 2), true,
+ LinearFunction(0, 0, 1, 1));
+}
+
+} // namespace
+} // namespace media
+} // namespace mojo
diff --git a/services/media/common/test/ratio_test.cc b/services/media/common/test/ratio_test.cc
new file mode 100644
index 0000000..8a0ce3e
--- /dev/null
+++ b/services/media/common/test/ratio_test.cc
@@ -0,0 +1,181 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <limits>
+
+#include "mojo/services/media/common/cpp/ratio.h"
+#include "services/media/common/test/test_base.h"
+
+namespace mojo {
+namespace media {
+namespace {
+
+class RatioTest : public TestBase {
+ static uint32_t gcd(uint32_t a, uint32_t b) {
+ while (b != 0) {
+ uint32_t t = a;
+ a = b;
+ b = t % b;
+ }
+ return a;
+ }
+
+ public:
+ // Verifies Ratio::Reduce and the constructor, ensuring that the ratio
+ // numerator * common_factor / denominator * common_factor is reduced to
+ // numerator / denominator. numerator and denominator need to be relatively
+ // prime for this to work.
+ void VerifyReduce(uint32_t numerator,
+ uint32_t denominator,
+ uint32_t common_factor) {
+ // Make sure numerator and denominator are relatively prime.
+ EXPECT_EQ(1u, gcd(numerator, denominator));
+
+ uint32_t test_numerator = numerator * common_factor;
+ uint32_t test_denominator = denominator * common_factor;
+
+ // Make sure the constructor reduces.
+ Ratio ratio(test_numerator, test_denominator);
+ EXPECT_EQ(numerator, ratio.numerator());
+ EXPECT_EQ(denominator, ratio.denominator());
+
+ // Test the static method.
+ Ratio::Reduce(&test_numerator, &test_denominator);
+ EXPECT_EQ(numerator, test_numerator);
+ EXPECT_EQ(denominator, test_denominator);
+ }
+
+ // Verifies the Ratio::Scale methods by scaling value by numerator /
+ // denominator and verifying the result.
+ void VerifyScale(int64_t value,
+ uint32_t numerator,
+ uint32_t denominator,
+ int64_t result) {
+ // Test the instance method.
+ EXPECT_EQ(result, Ratio(numerator, denominator).Scale(value));
+
+ // Test the static method.
+ EXPECT_EQ(result, Ratio::Scale(value, numerator, denominator));
+
+ // Test the operators.
+ EXPECT_EQ(result, value * Ratio(numerator, denominator));
+ EXPECT_EQ(result, Ratio(numerator, denominator) * value);
+ if (numerator != 0) {
+ EXPECT_EQ(result, value / Ratio(denominator, numerator));
+ }
+ }
+
+ // Verifies the Ratio::Product methods by multiplying the given a and b
+ // ratios and checking the result against the expected ratio.
+ void VerifyProduct(uint32_t a_numerator,
+ uint32_t a_denominator,
+ uint32_t b_numerator,
+ uint32_t b_denominator,
+ uint32_t expected_numerator,
+ uint32_t expected_denominator,
+ bool exact) {
+ // Test the first static method.
+ uint32_t actual_numerator;
+ uint32_t actual_denominator;
+ Ratio::Product(a_numerator, a_denominator, b_numerator, b_denominator,
+ &actual_numerator, &actual_denominator, exact);
+ EXPECT_EQ(expected_numerator, actual_numerator);
+ EXPECT_EQ(expected_denominator, actual_denominator);
+
+ // Test the second static method.
+ EXPECT_EQ(Ratio(expected_numerator, expected_denominator),
+ Ratio::Product(Ratio(a_numerator, a_denominator),
+ Ratio(b_numerator, b_denominator), exact));
+
+ // Test the operator
+ if (exact) {
+ EXPECT_EQ(Ratio(expected_numerator, expected_denominator),
+ Ratio(a_numerator, a_denominator) *
+ Ratio(b_numerator, b_denominator));
+ }
+ }
+
+ // Verifies the Ration::Inverse method using the given ratio.
+ void VerifyInverse(uint32_t numerator, uint32_t denominator) {
+ Ratio ratio(numerator, denominator);
+ Ratio inverse(ratio.Inverse());
+ EXPECT_EQ(ratio.denominator(), inverse.numerator());
+ EXPECT_EQ(ratio.numerator(), inverse.denominator());
+ }
+};
+
+// Tests Ratio::Reduce and that the Ratio constructor reduces.
+TEST_F(RatioTest, Reduce) {
+ VerifyReduce(0, 1, 1);
+ VerifyReduce(1, 1, 1);
+ VerifyReduce(1234, 1, 1);
+ VerifyReduce(1, 1234, 14);
+ VerifyReduce(1, 1, 1234);
+ VerifyReduce(10, 1, 1234);
+ VerifyReduce(1, 10, 1234);
+ VerifyReduce(49, 81, 1);
+ VerifyReduce(49, 81, 10);
+ VerifyReduce(49, 81, 100);
+ VerifyReduce(1, 8, 65536);
+ VerifyReduce(8, 1, 65536);
+}
+
+// Tests Ratio::Scale, static, instance and operator versions.
+TEST_F(RatioTest, Scale) {
+ const int64_t int64_min = std::numeric_limits<int64_t>::min();
+ VerifyScale(0, 0, 1, 0);
+ VerifyScale(1, 0, 1, 0);
+ VerifyScale(0, 1, 1, 0);
+ VerifyScale(1, 1, 1, 1);
+ VerifyScale(1, 2, 1, 2);
+ VerifyScale(1, 1, 2, 0);
+ VerifyScale(-1, 1, 2, -1);
+ VerifyScale(1000, 1, 2, 500);
+ VerifyScale(1001, 1, 2, 500);
+ VerifyScale(-1000, 1, 2, -500);
+ VerifyScale(-1001, 1, 2, -501);
+ VerifyScale(1000, 2, 1, 2000);
+ VerifyScale(1001, 2, 1, 2002);
+ VerifyScale(-1000, 2, 1, -2000);
+ VerifyScale(-1001, 2, 1, -2002);
+ VerifyScale(1ll << 32, 1, 1, 1ll << 32);
+ VerifyScale(1ll << 32, 1, 2, 1ll << 31);
+ VerifyScale(1ll << 32, 2, 1, 1ll << 33);
+ VerifyScale(1234ll << 30, 1, 1, 1234ll << 30);
+ VerifyScale(1234ll << 30, 1, 2, 1234ll << 29);
+ VerifyScale(1234ll << 30, 2, 1, 1234ll << 31);
+ VerifyScale(1234ll << 30, 1 << 31, 1, Ratio::kOverflow);
+ VerifyScale(1234ll << 30, 1ll << 31, (1ll << 31) - 2,
+ (1234ll << 30) + 1234ll);
+ VerifyScale(int64_min, 1, 1, int64_min);
+ VerifyScale(int64_min, 1, 2, int64_min / 2);
+ VerifyScale(int64_min / 2, 2, 1, int64_min);
+ VerifyScale(int64_min, 1000001, 1000000, Ratio::kOverflow);
+}
+
+// Tests Ratio::Product, static and operator versions.
+TEST_F(RatioTest, Product) {
+ VerifyProduct(0, 1, 0, 1, 0, 1, true);
+ VerifyProduct(1, 1, 1, 1, 1, 1, true);
+ VerifyProduct(10, 1, 1, 10, 1, 1, true);
+ VerifyProduct(4321, 1234, 617, 4321, 1, 2, true);
+ VerifyProduct(1234, 4321, 4321, 617, 2, 1, true);
+ VerifyProduct(1ll << 31, (1ll << 31) - 1, (1ll << 31) - 1, 1ll << 31, 1, 1,
+ true);
+ VerifyProduct(1ll << 31, (1ll << 31) - 1, (1ll << 31) - 2, 1ll << 31,
+ 0x7ffffffe, 0x7fffffff, false);
+}
+
+// Tests Ratio::Inverse.
+TEST_F(RatioTest, Inverse) {
+ VerifyInverse(1, 1);
+ VerifyInverse(2, 1);
+ VerifyInverse(1, 2);
+ VerifyInverse(1000000, 1234);
+ VerifyInverse(1234, 1000000);
+}
+
+} // namespace
+} // namespace media
+} // namespace mojo
diff --git a/services/media/common/test/test_base.h b/services/media/common/test/test_base.h
new file mode 100644
index 0000000..42c61cc
--- /dev/null
+++ b/services/media/common/test/test_base.h
@@ -0,0 +1,27 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef MOJO_SERVICES_MEDIA_COMMON_CPP_TEST_TEST_BASE_H_
+#define MOJO_SERVICES_MEDIA_COMMON_CPP_TEST_TEST_BASE_H_
+
+#include "mojo/public/cpp/application/application_test_base.h"
+
+namespace mojo {
+namespace media {
+namespace {
+
+class TestBase : public test::ApplicationTestBase {
+ public:
+ TestBase() {}
+ ~TestBase() override {}
+
+ private:
+ MOJO_DISALLOW_COPY_AND_ASSIGN(TestBase);
+};
+
+} // namespace
+} // namespace media
+} // namespace mojo
+
+#endif // MOJO_SERVICES_MEDIA_COMMON_CPP_TEST_TEST_BASE_H_