From 5b0e7e506510237aa1a079cae410219f06f591b8 Mon Sep 17 00:00:00 2001 From: rjo12 <119473038+rjo12@users.noreply.github.com> Date: Thu, 11 May 2023 22:49:23 +0930 Subject: [PATCH] Bugfix: Off-by-one bug in subtracting UINT. Fixes #1557 --- .../opentracks/sensors/UintUtilsTest.java | 39 ++++++++++++++++--- .../opentracks/sensors/UintUtils.java | 3 +- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/androidTest/java/de/dennisguse/opentracks/sensors/UintUtilsTest.java b/src/androidTest/java/de/dennisguse/opentracks/sensors/UintUtilsTest.java index 13926fcb6..63d40834a 100644 --- a/src/androidTest/java/de/dennisguse/opentracks/sensors/UintUtilsTest.java +++ b/src/androidTest/java/de/dennisguse/opentracks/sensors/UintUtilsTest.java @@ -11,15 +11,44 @@ public class UintUtilsTest { assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT16_MAX)); assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT16_MAX)); assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT16_MAX)); - assertEquals(65534, UintUtils.diff(1, 2, UintUtils.UINT16_MAX)); -// assertEquals(65535, UintUtils.diff(0, 1, UintUtils.UINT16_MAX)); - assertEquals(65535, UintUtils.UINT16_MAX); - + assertEquals(65535, UintUtils.diff(1, 2, UintUtils.UINT16_MAX)); /* unsigned 16 arithmetic is modulo UINT16_MAX + 1, not modulo UINT16_MAX */ assertEquals(65530, UintUtils.diff(UintUtils.UINT16_MAX, 5, UintUtils.UINT16_MAX)); + + /* Test modulo arithmetic for arguments that are out of range */ + + if (false) { /* false means UintUtils.diff() throws if arguments out of range */ + assertEquals(0, UintUtils.diff(65537, 1, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(1, 65537, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(65537, 65537, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(-65535, 1, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(1, -65535, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(-65535, -65535, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(-65535, 65537, UintUtils.UINT16_MAX)); + assertEquals(0, UintUtils.diff(65537, -65535, UintUtils.UINT16_MAX)); + } + + /* The following tests are the above, but for 32-bit unsigned. */ + + assertEquals(0, UintUtils.diff(1, 1, UintUtils.UINT32_MAX)); + assertEquals(1, UintUtils.diff(2, 1, UintUtils.UINT32_MAX)); + assertEquals(3, UintUtils.diff(5, 2, UintUtils.UINT32_MAX)); + assertEquals(4294967295L, UintUtils.diff(1, 2, UintUtils.UINT32_MAX)); + assertEquals(4294967290L, UintUtils.diff(UintUtils.UINT32_MAX, 5, UintUtils.UINT32_MAX)); + + if (false) { /* false means UintUtils.diff() throws if arguments out of range */ + assertEquals(0, UintUtils.diff(4294967297L, 1, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(1, 4294967297L, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(4294967297L, 4294967297L, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(-4294967295L, 1, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(1, -4294967295L, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(-4294967295L, -4294967295L, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(-4294967295L, 4294967297L, UintUtils.UINT32_MAX)); + assertEquals(0, UintUtils.diff(65537, -4294967297L, UintUtils.UINT32_MAX)); + } } @Test public void realData() { assertEquals(1, UintUtils.diff(381616, 381615, UintUtils.UINT32_MAX)); } -} \ No newline at end of file +} diff --git a/src/main/java/de/dennisguse/opentracks/sensors/UintUtils.java b/src/main/java/de/dennisguse/opentracks/sensors/UintUtils.java index c4610cd22..de76428f2 100644 --- a/src/main/java/de/dennisguse/opentracks/sensors/UintUtils.java +++ b/src/main/java/de/dennisguse/opentracks/sensors/UintUtils.java @@ -14,6 +14,7 @@ public class UintUtils { * @return diff */ public static long diff(long a, long b, final long UINT_MAX) { + if (a < 0 || b < 0) { throw new RuntimeException("a or b cannot be less than zero."); } @@ -25,6 +26,6 @@ public class UintUtils { return a - b; } - return (UINT_MAX - b) + a; + return (UINT_MAX + 1 - b) + a; } }