Internal change PiperOrigin-RevId: 1000629467
diff --git a/java/core/src/main/java/com/google/protobuf/ByteString.java b/java/core/src/main/java/com/google/protobuf/ByteString.java index 1c44438..ac50459 100644 --- a/java/core/src/main/java/com/google/protobuf/ByteString.java +++ b/java/core/src/main/java/com/google/protobuf/ByteString.java
@@ -1610,10 +1610,10 @@ @Override protected final boolean equalsInternal(ByteString other) { - if (other instanceof LiteralByteString) { - return Arrays.equals(bytes, ((LiteralByteString) other).bytes); - } else if (other instanceof BoundedByteString) { - return equalsRange(other, 0, size()); + if (other instanceof LiteralByteString lbsOther) { + return Arrays.equals(bytes, lbsOther.bytes); + } else if (other instanceof BoundedByteString bbsOther) { + return subArrayEquals(bytes, 0, bbsOther.bytes, bbsOther.offset, bytes.length); } else { // Anything other than LiteralByteString or BoundedByteString, delegate to the other side // who will be able to optimize the check better based on the more exotic representation. @@ -1806,10 +1806,10 @@ @Override protected boolean equalsInternal(ByteString other) { - // If the other side is a LiteralByteString or BoundedByteString, implement equals by doing - // equalsInternal since that is as a fast range check against the byte arrays. - if (other instanceof LiteralByteString || other instanceof BoundedByteString) { - return equalsRange(other, 0, size()); + if (other instanceof LiteralByteString lbsOther) { + return subArrayEquals(bytes, offset, lbsOther.bytes, 0, length); + } else if (other instanceof BoundedByteString bbsOther) { + return subArrayEquals(bytes, offset, bbsOther.bytes, bbsOther.offset, length); } // Anything other than LiteralByteString or BoundedByteString, delegate to the other side
diff --git a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java index c4e6c6a..b3585c8 100644 --- a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
@@ -815,6 +815,58 @@ } @Test + public void testEquals_boundedByteString_matchingAndMismatching() { + int length = 256; + byte[] base = getTestBytes(length + 20, 998877L); + ByteString bounded1 = ByteString.copyFrom(base).substring(5, 5 + length); + ByteString bounded2 = ByteString.copyFrom(base).substring(10, 10 + length); + // Construct another BoundedByteString at a different offset with identical contents. + byte[] shifted = new byte[length + 20]; + System.arraycopy(base, 5, shifted, 12, length); + ByteString boundedSame = ByteString.copyFrom(shifted).substring(12, 12 + length); + ByteString literalSame = ByteString.copyFrom(base, 5, length); + ByteString ropeSame = + RopeByteString.newInstanceForTest( + literalSame.substring(0, 100), literalSame.substring(100, length)); + + // Equal across representations (both directions) + assertThat(bounded1).isEqualTo(boundedSame); + assertThat(boundedSame).isEqualTo(bounded1); + assertThat(bounded1).isEqualTo(literalSame); + assertThat(literalSame).isEqualTo(bounded1); + assertThat(bounded1).isEqualTo(ropeSame); + assertThat(ropeSame).isEqualTo(bounded1); + assertThat(bounded1).isNotEqualTo(bounded2); + + // Mismatches at start, middle (across SIMD word boundary), and end + for (int mismatchPos : new int[] {0, 1, 7, 8, 31, 32, length / 2, length - 1}) { + byte[] mutated = Arrays.copyOf(shifted, shifted.length); + mutated[12 + mismatchPos] ^= 1; + ByteString boundedDiff = ByteString.copyFrom(mutated).substring(12, 12 + length); + ByteString literalDiff = ByteString.copyFrom(mutated, 12, length); + ByteString ropeDiff = + RopeByteString.newInstanceForTest( + literalDiff.substring(0, 100), literalDiff.substring(100, length)); + + assertWithMessage("Bounded vs Bounded mismatch at %s", mismatchPos) + .that(bounded1.equals(boundedDiff)) + .isFalse(); + assertWithMessage("Bounded vs Literal mismatch at %s", mismatchPos) + .that(bounded1.equals(literalDiff)) + .isFalse(); + assertWithMessage("Literal vs Bounded mismatch at %s", mismatchPos) + .that(literalDiff.equals(bounded1)) + .isFalse(); + assertWithMessage("Bounded vs Rope mismatch at %s", mismatchPos) + .that(bounded1.equals(ropeDiff)) + .isFalse(); + assertWithMessage("Rope vs Bounded mismatch at %s", mismatchPos) + .that(ropeDiff.equals(bounded1)) + .isFalse(); + } + } + + @Test public void testSubstringNoCopyParity() { byte[] bigBytes = getTestBytes(2048 * 1024, 113344L); int start = 512 * 1024 - 3333;