From d711e077b7b2986277a60a68980e55396c002819 Mon Sep 17 00:00:00 2001 From: Moritz Zwerger Date: Mon, 31 Mar 2025 14:43:56 +0200 Subject: [PATCH] position datatypes: fix overflow when adding from -1 --- .../data/world/positions/BlockPosition.kt | 35 +++++++++++----- .../data/world/positions/ChunkPosition.kt | 26 +++++++++--- .../data/world/positions/InChunkPosition.kt | 15 +++++-- .../data/world/positions/SectionPosition.kt | 37 ++++++++++++----- .../data/world/positions/BlockPositionTest.kt | 40 ++++++++++++++++++- .../data/world/positions/ChunkPositionTest.kt | 12 ++++++ .../world/positions/InChunkPositionTest.kt | 6 +++ .../world/positions/SectionPositionTest.kt | 19 ++++++++- 8 files changed, 160 insertions(+), 30 deletions(-) diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt index 9d3af0e10..83236f563 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/BlockPosition.kt @@ -39,50 +39,67 @@ value class BlockPosition( inline val y: Int get() = (((raw ushr SHIFT_Y).toInt() and MASK_Y) shl (Int.SIZE_BITS - BITS_Y)) shr (Int.SIZE_BITS - BITS_Y) inline val z: Int get() = (((raw ushr SHIFT_Z).toInt() and MASK_Z) shl (Int.SIZE_BITS - BITS_Z)) shr (Int.SIZE_BITS - BITS_Z) + inline fun modify(other: Long, component: Long, add: Long): BlockPosition { + val bc = raw and other + val a = ((raw and component) + add) and component + return BlockPosition(bc or a) + } + + inline fun modifyX(modify: Long): BlockPosition { + return modify((Integer.toUnsignedLong(MASK_Y) shl SHIFT_Y) or (Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z), Integer.toUnsignedLong(MASK_X) shl SHIFT_X, modify) + } + + inline fun modifyY(modify: Long): BlockPosition { + return modify((Integer.toUnsignedLong(MASK_X) shl SHIFT_X) or (Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z), Integer.toUnsignedLong(MASK_Y) shl SHIFT_Y, modify) + } + + inline fun modifyZ(modify: Long): BlockPosition { + return modify((Integer.toUnsignedLong(MASK_X) shl SHIFT_X) or (Integer.toUnsignedLong(MASK_Y) shl SHIFT_Y), Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z, modify) + } inline fun plusX(): BlockPosition { assertPosition(this.x < MAX_X) - return BlockPosition(raw + X * 1) + return modifyX(X * 1) } inline fun plusX(x: Int): BlockPosition { assertPosition(this.x + x, -MAX_X, MAX_X) - return BlockPosition(raw + X * x) + return modifyX(X * x) } inline fun minusX(): BlockPosition { assertPosition(this.x > -MAX_X) - return BlockPosition(raw - X * 1) + return modifyX(-X * 1) } inline fun plusY(): BlockPosition { assertPosition(this.y < MAX_Y) - return BlockPosition(raw + Y * 1) + return modifyY(Y * 1) } inline fun plusY(y: Int): BlockPosition { assertPosition(this.y + y, MIN_Y, MAX_Y) - return BlockPosition(raw + Y * y) + return modifyY(Y * y) } inline fun minusY(): BlockPosition { assertPosition(this.y > -MAX_Y) - return BlockPosition(raw - Y * 1) + return modifyY(-Y * 1) } inline fun plusZ(): BlockPosition { assertPosition(this.z < MAX_Y) - return BlockPosition(raw + Z * 1) + return modifyZ(Z * 1) } inline fun plusZ(z: Int): BlockPosition { assertPosition(this.z + z, -MAX_Z, MAX_Z) - return BlockPosition(raw + Z * z) + return modifyZ(Z * z) } inline fun minusZ(): BlockPosition { assertPosition(this.z > -MAX_Z) - return BlockPosition(raw - Z * 1) + return modifyZ(-Z * 1) } inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = BlockPosition(x, y, z) diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt index 97c4c47a5..c1b5b3a1d 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/ChunkPosition.kt @@ -34,35 +34,49 @@ value class ChunkPosition( inline val x: Int get() = (raw ushr SHIFT_X).toInt() and MASK_X inline val z: Int get() = (raw ushr SHIFT_Z).toInt() and MASK_Z + inline fun modify(other: Long, component: Long, add: Long): ChunkPosition { + val bc = raw and other + val a = ((raw and component) + add) and component + return ChunkPosition(bc or a) + } + + inline fun modifyX(modify: Long): ChunkPosition { + return modify(Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z, Integer.toUnsignedLong(MASK_X) shl SHIFT_X, modify) + } + + inline fun modifyZ(modify: Long): ChunkPosition { + return modify(Integer.toUnsignedLong(MASK_X) shl SHIFT_X, Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z, modify) + } + inline fun plusX(): ChunkPosition { assertPosition(this.x < MAX_X) - return ChunkPosition(raw + X * 1) + return modifyX(X * 1) } inline fun plusX(x: Int): ChunkPosition { assertPosition(this.x + x, -MAX_X, MAX_X) - return ChunkPosition(raw + X * x) + return modifyX(X * x) } inline fun minusX(): ChunkPosition { assert(this.x > -MAX_X) - return ChunkPosition(raw - X * 1) + return modifyX(-X * 1) } inline fun plusZ(): ChunkPosition { assert(this.z < MAX_Z) - return ChunkPosition(raw + Z * 1) + return modifyZ(Z * 1) } inline fun plusZ(z: Int): ChunkPosition { assertPosition(this.z + z, -MAX_Z, MAX_Z) - return ChunkPosition(raw + Z * z) + return modifyZ(Z * z) } inline fun minusZ(): ChunkPosition { assert(this.z > -MAX_Z) - return ChunkPosition(raw - Z * 1) + return modifyZ(-Z * 1) } inline fun with(x: Int = this.x, z: Int = this.z) = ChunkPosition(x, z) diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt index aedcb6489..18acfc7d0 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/InChunkPosition.kt @@ -39,6 +39,15 @@ value class InChunkPosition( inline val z: Int get() = (raw ushr SHIFT_Z) and MASK_Z inline val xz: Int get() = raw and ((MASK_X shl SHIFT_X) or (MASK_Z shl SHIFT_Z)) + inline fun modify(other: Int, component: Int, add: Int): InChunkPosition { + val bc = raw and other + val a = ((raw and component) + add) and component + return InChunkPosition(bc or a) + } + + inline fun modifyY(modify: Int): InChunkPosition { + return modify((MASK_X shl SHIFT_X) or (MASK_Z shl SHIFT_Z), MASK_Y shl SHIFT_Y, modify) + } inline fun plusX(): InChunkPosition { assertPosition(this.x < ProtocolDefinition.SECTION_MAX_X) @@ -57,17 +66,17 @@ value class InChunkPosition( inline fun plusY(): InChunkPosition { assertPosition(this.y < ProtocolDefinition.CHUNK_MAX_Y) - return InChunkPosition(raw + Y * 1) + return modifyY(Y * 1) } inline fun plusY(y: Int): InChunkPosition { assertPosition(this.y + y, ProtocolDefinition.CHUNK_MIN_Y, ProtocolDefinition.CHUNK_MAX_Y) - return InChunkPosition(raw + Y * y) + return modifyY(Y * y) } inline fun minusY(): InChunkPosition { assertPosition(this.y > ProtocolDefinition.CHUNK_MIN_Y) - return InChunkPosition(raw - Y * 1) + return modifyY(-Y * 1) } inline fun plusZ(): InChunkPosition { diff --git a/src/main/java/de/bixilon/minosoft/data/world/positions/SectionPosition.kt b/src/main/java/de/bixilon/minosoft/data/world/positions/SectionPosition.kt index 952e37c35..00ea970c5 100644 --- a/src/main/java/de/bixilon/minosoft/data/world/positions/SectionPosition.kt +++ b/src/main/java/de/bixilon/minosoft/data/world/positions/SectionPosition.kt @@ -35,49 +35,68 @@ value class SectionPosition( inline val y: SectionHeight get() = (((raw ushr SHIFT_Y).toInt() and MASK_Y) shl (Int.SIZE_BITS - BITS_Y)) shr (Int.SIZE_BITS - BITS_Y) inline val z: Int get() = (((raw ushr SHIFT_Z).toInt() and MASK_Z) shl (Int.SIZE_BITS - BITS_Z)) shr (Int.SIZE_BITS - BITS_Z) + + inline fun modify(other: Long, component: Long, add: Long): SectionPosition { + val bc = raw and other + val a = ((raw and component) + add) and component + return SectionPosition(bc or a) + } + + inline fun modifyX(modify: Long): SectionPosition { + return modify((Integer.toUnsignedLong(MASK_Y) shl SHIFT_Y) or (Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z), Integer.toUnsignedLong(MASK_X) shl SHIFT_X, modify) + } + + inline fun modifyY(modify: Long): SectionPosition { + return modify((Integer.toUnsignedLong(MASK_X) shl SHIFT_X) or (Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z), Integer.toUnsignedLong(MASK_Y) shl SHIFT_Y, modify) + } + + inline fun modifyZ(modify: Long): SectionPosition { + return modify((Integer.toUnsignedLong(MASK_X) shl SHIFT_X) or (Integer.toUnsignedLong(MASK_Y) shl SHIFT_Y), Integer.toUnsignedLong(MASK_Z) shl SHIFT_Z, modify) + } + inline fun plusX(): SectionPosition { assertPosition(this.x < MAX_X) - return SectionPosition(raw + X * 1) + return modifyX(X * 1) } inline fun plusX(x: Int): SectionPosition { assertPosition(this.x + x, -MAX_X, MAX_X) - return SectionPosition(raw + X * x) + return modifyX(X * x) } inline fun minusX(): SectionPosition { assert(this.x > -MAX_X) - return SectionPosition(raw - X * 1) + return modifyX(-X * 1) } inline fun plusY(): SectionPosition { assertPosition(this.y < MAX_Y) - return SectionPosition(raw + Y * 1) + return modifyY(Y * 1) } inline fun plusY(y: Int): SectionPosition { assertPosition(this.y + y, MIN_Y, MAX_Y) - return SectionPosition(raw + Y * y) + return modifyY(Y * y) } inline fun minusY(): SectionPosition { assert(this.y > MIN_Y) - return SectionPosition(raw - Y * 1) + return modifyY(-Y * 1) } inline fun plusZ(): SectionPosition { assert(this.z < MAX_Z) - return SectionPosition(raw + Z * 1) + return modifyZ(Z * 1) } inline fun plusZ(z: Int): SectionPosition { assertPosition(this.z + z, -MAX_Z, MAX_Z) - return SectionPosition(raw + Z * z) + return modifyZ(Z * z) } inline fun minusZ(): SectionPosition { assert(this.z > -MAX_Z) - return SectionPosition(raw - Z * 1) + return modifyZ(-Z * 1) } inline fun with(x: Int = this.x, y: Int = this.y, z: Int = this.z) = SectionPosition(x, y, z) diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt index 4284ae5e7..d9da6163a 100644 --- a/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/BlockPositionTest.kt @@ -66,6 +66,18 @@ class BlockPositionTest { assertEquals(position.plusX().x, 3) } + @Test + fun `correct plus negative x`() { + val position = BlockPosition(-2, 0xF, 0xF).plusX() + assertEquals(position, BlockPosition(-1, 0xF, 0xF)) + } + + @Test + fun `correct plus zero x`() { + val position = BlockPosition(-1, 0xF, 0xF).plusX() + assertEquals(position, BlockPosition(0, 0xF, 0xF)) + } + @Test fun `correct plus 2 x`() { val position = BlockPosition(2, 0xF, 0xF) @@ -114,6 +126,18 @@ class BlockPositionTest { assertEquals(position.plusY(2).y, 4) } + @Test + fun `correct plus negative y`() { + val position = BlockPosition(0xF, -2, 0xF).plusY() + assertEquals(position, BlockPosition(0xF, -1, 0xF)) + } + + @Test + fun `correct plus zero y`() { + val position = BlockPosition(0xF, -1, 0xF).plusY() + assertEquals(position, BlockPosition(0xF, 0, 0xF)) + } + @Test fun `correct minus y`() { val position = BlockPosition(0xF, 2, 0xF) @@ -153,8 +177,20 @@ class BlockPositionTest { @Test fun `correct plus 2 z`() { - val position = BlockPosition(0xF, 0xF, 2) - assertEquals(position.plusZ(2).z, 4) + val position = BlockPosition(0xF, 0xF, 2).plusZ(2) + assertEquals(position, BlockPosition(0xF, 0xF, 4)) + } + + @Test + fun `correct plus negative z`() { + val position = BlockPosition(0xF, 0xF, -2).plusZ() + assertEquals(position, BlockPosition(0xF, 0xF, -1)) + } + + @Test + fun `correct plus zero z`() { + val position = BlockPosition(0xF, 0xF, -1).plusZ() + assertEquals(position, BlockPosition(0xF, 0xF, 0)) } @Test diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt index 547412df9..85c60b48b 100644 --- a/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/ChunkPositionTest.kt @@ -66,6 +66,12 @@ class ChunkPositionTest { assertEquals(position.plusX().x, 3) } + @Test + fun `correct plus negative x`() { + val position = ChunkPosition(-1, 0xF).plusX() + assertEquals(position, ChunkPosition(0, 0xF)) + } + @Test fun `correct plus 2 x`() { val position = ChunkPosition(2, 0xF) @@ -108,6 +114,12 @@ class ChunkPositionTest { assertEquals(position.plusZ().z, 3) } + @Test + fun `correct plus negative z`() { + val position = ChunkPosition(0xF, -1).plusZ() + assertEquals(position, ChunkPosition(0xF, 0)) + } + @Test fun `correct plus 2 z`() { val position = ChunkPosition(0xF, 2) diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt index b6db01361..03379671f 100644 --- a/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/InChunkPositionTest.kt @@ -85,6 +85,12 @@ class InChunkPositionTest { assertEquals(position.plusY().y, 3) } + @Test + fun `correct plus negative y`() { + val position = InChunkPosition(0xF, -1, 0xF).plusY() + assertEquals(position, InChunkPosition(0xF, 0, 0xF)) + } + @Test fun `correct plus 2 y`() { val position = InChunkPosition(0xF, 2, 0xF) diff --git a/src/test/java/de/bixilon/minosoft/data/world/positions/SectionPositionTest.kt b/src/test/java/de/bixilon/minosoft/data/world/positions/SectionPositionTest.kt index 77efafc19..dbab8ad67 100644 --- a/src/test/java/de/bixilon/minosoft/data/world/positions/SectionPositionTest.kt +++ b/src/test/java/de/bixilon/minosoft/data/world/positions/SectionPositionTest.kt @@ -66,6 +66,12 @@ class SectionPositionTest { assertEquals(position.plusX().x, 3) } + @Test + fun `correct plus negative x`() { + val position = SectionPosition(-1, 0xF, 0xF).plusX() + assertEquals(position, SectionPosition(0, 0xF, 0xF)) + } + @Test fun `correct plus 2 x`() { val position = SectionPosition(2, 0xF, 0xF) @@ -108,6 +114,12 @@ class SectionPositionTest { assertEquals(position.plusY().y, 3) } + @Test + fun `correct plus negative y`() { + val position = SectionPosition(0xF, -1, 0xF).plusY() + assertEquals(position, SectionPosition(0xF, 0, 0xF)) + } + @Test fun `correct plus 2 y`() { val position = SectionPosition(0xF, 2, 0xF) @@ -144,13 +156,18 @@ class SectionPositionTest { assertEquals(position.z, -1875000) } - @Test fun `correct plus z`() { val position = SectionPosition(0xF, 0xF, 2) assertEquals(position.plusZ().z, 3) } + @Test + fun `correct plus negative z`() { + val position = SectionPosition(0xF, 0xF, -1).plusZ() + assertEquals(position, SectionPosition(0xF, 0xF, 0)) + } + @Test fun `correct plus 2 z`() { val position = SectionPosition(0xF, 0xF, 2)