mirror of
https://github.com/androidx/media.git
synced 2025-04-30 06:46:50 +08:00
Fix Util.scaleLargeValue/Timestamp
to handle negative numbers
#minor-release PiperOrigin-RevId: 570337535
This commit is contained in:
parent
fa51f8cd4d
commit
9edbfa974a
@ -1840,12 +1840,13 @@ public final class Util {
|
|||||||
// Directly multiplying value and multiplier will overflow a long, so we try and cancel
|
// Directly multiplying value and multiplier will overflow a long, so we try and cancel
|
||||||
// with GCD and try directly multiplying again below. If that still overflows we fall
|
// with GCD and try directly multiplying again below. If that still overflows we fall
|
||||||
// through to floating point arithmetic.
|
// through to floating point arithmetic.
|
||||||
long gcdOfMultiplierAndDivisor = LongMath.gcd(multiplier, divisor);
|
long gcdOfMultiplierAndDivisor = LongMath.gcd(Math.abs(multiplier), Math.abs(divisor));
|
||||||
long simplifiedMultiplier =
|
long simplifiedMultiplier =
|
||||||
LongMath.divide(multiplier, gcdOfMultiplierAndDivisor, RoundingMode.UNNECESSARY);
|
LongMath.divide(multiplier, gcdOfMultiplierAndDivisor, RoundingMode.UNNECESSARY);
|
||||||
long simplifiedDivisor =
|
long simplifiedDivisor =
|
||||||
LongMath.divide(divisor, gcdOfMultiplierAndDivisor, RoundingMode.UNNECESSARY);
|
LongMath.divide(divisor, gcdOfMultiplierAndDivisor, RoundingMode.UNNECESSARY);
|
||||||
long gcdOfValueAndSimplifiedDivisor = LongMath.gcd(value, simplifiedDivisor);
|
long gcdOfValueAndSimplifiedDivisor =
|
||||||
|
LongMath.gcd(Math.abs(value), Math.abs(simplifiedDivisor));
|
||||||
long simplifiedValue =
|
long simplifiedValue =
|
||||||
LongMath.divide(value, gcdOfValueAndSimplifiedDivisor, RoundingMode.UNNECESSARY);
|
LongMath.divide(value, gcdOfValueAndSimplifiedDivisor, RoundingMode.UNNECESSARY);
|
||||||
simplifiedDivisor =
|
simplifiedDivisor =
|
||||||
|
@ -18,11 +18,14 @@ package androidx.media3.common.util;
|
|||||||
import static androidx.media3.common.util.Assertions.checkState;
|
import static androidx.media3.common.util.Assertions.checkState;
|
||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assume.assumeFalse;
|
||||||
import static org.junit.Assume.assumeTrue;
|
import static org.junit.Assume.assumeTrue;
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.math.LongMath;
|
import com.google.common.math.LongMath;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
import org.robolectric.ParameterizedRobolectricTestRunner;
|
import org.robolectric.ParameterizedRobolectricTestRunner;
|
||||||
@ -45,54 +48,92 @@ public class UtilScaleLargeValueParameterizedTest {
|
|||||||
|
|
||||||
@Parameters(name = "{0}")
|
@Parameters(name = "{0}")
|
||||||
public static ImmutableList<Object[]> implementations() {
|
public static ImmutableList<Object[]> implementations() {
|
||||||
return ImmutableList.of(
|
ImmutableList<Implementation> implementations =
|
||||||
new Object[] {"single-value", (ScaleLargeValueFn) Util::scaleLargeValue},
|
ImmutableList.of(
|
||||||
new Object[] {
|
new Implementation("single-value", Util::scaleLargeValue),
|
||||||
"list",
|
new Implementation(
|
||||||
(ScaleLargeValueFn)
|
"list",
|
||||||
(value, multiplier, divisor, roundingMode) ->
|
(value, multiplier, divisor, roundingMode) ->
|
||||||
Util.scaleLargeValues(ImmutableList.of(value), multiplier, divisor, roundingMode)[
|
Util.scaleLargeValues(
|
||||||
0]
|
ImmutableList.of(value), multiplier, divisor, roundingMode)[0]),
|
||||||
},
|
new Implementation(
|
||||||
new Object[] {
|
"array-in-place",
|
||||||
"array-in-place",
|
(value, multiplier, divisor, roundingMode) -> {
|
||||||
(ScaleLargeValueFn)
|
long[] values = new long[] {value};
|
||||||
|
Util.scaleLargeValuesInPlace(values, multiplier, divisor, roundingMode);
|
||||||
|
return values[0];
|
||||||
|
}),
|
||||||
|
new Implementation(
|
||||||
|
"single-timestamp",
|
||||||
|
(long timestamp, long multiplier, long divisor, RoundingMode roundingMode) -> {
|
||||||
|
assumeTrue(
|
||||||
|
roundingMode == RoundingMode.UNNECESSARY
|
||||||
|
|| roundingMode == RoundingMode.FLOOR);
|
||||||
|
return Util.scaleLargeTimestamp(timestamp, multiplier, divisor);
|
||||||
|
}),
|
||||||
|
new Implementation(
|
||||||
|
"timestamp-list",
|
||||||
|
(timestamp, multiplier, divisor, roundingMode) -> {
|
||||||
|
assumeTrue(
|
||||||
|
roundingMode == RoundingMode.UNNECESSARY
|
||||||
|
|| roundingMode == RoundingMode.FLOOR);
|
||||||
|
return Util.scaleLargeTimestamps(
|
||||||
|
ImmutableList.of(timestamp), multiplier, divisor)[0];
|
||||||
|
}),
|
||||||
|
new Implementation(
|
||||||
|
"timestamp-array-in-place",
|
||||||
|
(timestamp, multiplier, divisor, roundingMode) -> {
|
||||||
|
assumeTrue(
|
||||||
|
roundingMode == RoundingMode.UNNECESSARY
|
||||||
|
|| roundingMode == RoundingMode.FLOOR);
|
||||||
|
long[] timestamps = new long[] {timestamp};
|
||||||
|
Util.scaleLargeTimestampsInPlace(timestamps, multiplier, divisor);
|
||||||
|
return timestamps[0];
|
||||||
|
}));
|
||||||
|
|
||||||
|
List<Implementation> implementationsWithNegativeCases = new ArrayList<>();
|
||||||
|
for (Implementation implementation : implementations) {
|
||||||
|
implementationsWithNegativeCases.add(implementation);
|
||||||
|
implementationsWithNegativeCases.add(
|
||||||
|
new Implementation(
|
||||||
|
implementation.name + "-negative-value",
|
||||||
(value, multiplier, divisor, roundingMode) -> {
|
(value, multiplier, divisor, roundingMode) -> {
|
||||||
long[] values = new long[] {value};
|
assumeTrue(roundingMode == RoundingMode.UNNECESSARY);
|
||||||
Util.scaleLargeValuesInPlace(values, multiplier, divisor, roundingMode);
|
long result =
|
||||||
return values[0];
|
implementation.scaleFn.scaleLargeValue(
|
||||||
}
|
-value, multiplier, divisor, roundingMode);
|
||||||
},
|
assumeFalse(result == Long.MIN_VALUE || result == Long.MAX_VALUE);
|
||||||
new Object[] {
|
return -result;
|
||||||
"single-timestamp",
|
}));
|
||||||
(ScaleLargeValueFn)
|
implementationsWithNegativeCases.add(
|
||||||
(long timestamp, long multiplier, long divisor, RoundingMode roundingMode) -> {
|
new Implementation(
|
||||||
assumeTrue(
|
implementation.name + "-negative-multiplier",
|
||||||
roundingMode == RoundingMode.UNNECESSARY || roundingMode == RoundingMode.FLOOR);
|
(value, multiplier, divisor, roundingMode) -> {
|
||||||
return Util.scaleLargeTimestamp(timestamp, multiplier, divisor);
|
assumeTrue(roundingMode == RoundingMode.UNNECESSARY);
|
||||||
}
|
long result =
|
||||||
},
|
implementation.scaleFn.scaleLargeValue(
|
||||||
new Object[] {
|
value, -multiplier, divisor, roundingMode);
|
||||||
"timestamp-list",
|
assumeFalse(result == Long.MIN_VALUE || result == Long.MAX_VALUE);
|
||||||
(ScaleLargeValueFn)
|
return -result;
|
||||||
(timestamp, multiplier, divisor, roundingMode) -> {
|
}));
|
||||||
assumeTrue(
|
implementationsWithNegativeCases.add(
|
||||||
roundingMode == RoundingMode.UNNECESSARY || roundingMode == RoundingMode.FLOOR);
|
new Implementation(
|
||||||
return Util.scaleLargeTimestamps(ImmutableList.of(timestamp), multiplier, divisor)[
|
implementation.name + "-negative-divisor",
|
||||||
0];
|
(value, multiplier, divisor, roundingMode) -> {
|
||||||
}
|
assumeTrue(roundingMode == RoundingMode.UNNECESSARY);
|
||||||
},
|
long result =
|
||||||
new Object[] {
|
implementation.scaleFn.scaleLargeValue(
|
||||||
"timestamp-array-in-place",
|
value, multiplier, -divisor, roundingMode);
|
||||||
(ScaleLargeValueFn)
|
assumeFalse(result == Long.MIN_VALUE || result == Long.MAX_VALUE);
|
||||||
(timestamp, multiplier, divisor, roundingMode) -> {
|
return -result;
|
||||||
assumeTrue(
|
}));
|
||||||
roundingMode == RoundingMode.UNNECESSARY || roundingMode == RoundingMode.FLOOR);
|
}
|
||||||
long[] timestamps = new long[] {timestamp};
|
|
||||||
Util.scaleLargeTimestampsInPlace(timestamps, multiplier, divisor);
|
ImmutableList.Builder<Object[]> implementationsAsObjectArray = ImmutableList.builder();
|
||||||
return timestamps[0];
|
for (Implementation implementation : implementationsWithNegativeCases) {
|
||||||
}
|
implementationsAsObjectArray.add(new Object[] {implementation.name, implementation.scaleFn});
|
||||||
});
|
}
|
||||||
|
return implementationsAsObjectArray.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Every parameter has to be assigned to a field, even if it's only used to name the test.
|
// Every parameter has to be assigned to a field, even if it's only used to name the test.
|
||||||
@ -277,6 +318,16 @@ public class UtilScaleLargeValueParameterizedTest {
|
|||||||
assertThat(result).isEqualTo(Long.MAX_VALUE);
|
assertThat(result).isEqualTo(Long.MAX_VALUE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class Implementation {
|
||||||
|
public final String name;
|
||||||
|
public final ScaleLargeValueFn scaleFn;
|
||||||
|
|
||||||
|
private Implementation(String name, ScaleLargeValueFn scaleFn) {
|
||||||
|
this.name = name;
|
||||||
|
this.scaleFn = scaleFn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private interface ScaleLargeValueFn {
|
private interface ScaleLargeValueFn {
|
||||||
long scaleLargeValue(long value, long multiplier, long divisor, RoundingMode roundingMode);
|
long scaleLargeValue(long value, long multiplier, long divisor, RoundingMode roundingMode);
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user