Internal change PiperOrigin-RevId: 1000631407
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..49c1369 100644 --- a/java/core/src/test/java/com/google/protobuf/ByteStringTest.java +++ b/java/core/src/test/java/com/google/protobuf/ByteStringTest.java
@@ -133,25 +133,93 @@ } @Test + public void testCompare_boundedAndLiteralByteStrings() throws Exception { + Comparator<ByteString> comparator = ByteString.unsignedLexicographicalComparator(); + + // Underlying bytes with non-zero offsets and unsigned high bytes (0xFF = -1). + byte[] padded = new byte[] {99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, (byte) 0xFF, 99}; + ByteString literal1 = ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); + ByteString literal2 = ByteString.copyFrom(new byte[] {2, 3, 4, 5, 6, 7, 8, 9, 10, (byte) 0xFF}); + ByteString literalPrefix = ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5}); + + // Substring with non-zero offset creates BoundedByteString. + ByteString bounded1 = ByteString.copyFrom(padded).substring(1, 11); + ByteString bounded1Copy = ByteString.copyFrom(padded).substring(1, 11); + ByteString bounded2 = ByteString.copyFrom(padded).substring(2, 12); + ByteString boundedPrefix = ByteString.copyFrom(padded).substring(1, 6); + + // Literal vs Bounded + assertThat(comparator.compare(literal1, bounded1)).isEqualTo(0); + assertThat(comparator.compare(literal1, bounded2)).isLessThan(0); + assertThat(comparator.compare(literal2, bounded1)).isGreaterThan(0); + assertThat(comparator.compare(literalPrefix, bounded1)).isLessThan(0); + assertThat(comparator.compare(literal1, boundedPrefix)).isGreaterThan(0); + + // Bounded vs Literal + assertThat(comparator.compare(bounded1, literal1)).isEqualTo(0); + assertThat(comparator.compare(bounded1, literal2)).isLessThan(0); + assertThat(comparator.compare(bounded2, literal1)).isGreaterThan(0); + assertThat(comparator.compare(boundedPrefix, literal1)).isLessThan(0); + assertThat(comparator.compare(bounded1, literalPrefix)).isGreaterThan(0); + + // Bounded vs Bounded + assertThat(comparator.compare(bounded1, bounded1Copy)).isEqualTo(0); + assertThat(comparator.compare(bounded1, bounded2)).isLessThan(0); + assertThat(comparator.compare(bounded2, bounded1)).isGreaterThan(0); + assertThat(comparator.compare(boundedPrefix, bounded1)).isLessThan(0); + assertThat(comparator.compare(bounded1, boundedPrefix)).isGreaterThan(0); + + // Unsigned byte comparison across Bounded and Literal (0x0A < 0xFF) + ByteString boundedLow = ByteString.copyFrom(padded).substring(10, 11); // {10} + ByteString boundedHigh = ByteString.copyFrom(padded).substring(11, 12); // {-1} + ByteString literalLow = ByteString.copyFrom(new byte[] {10}); + ByteString literalHigh = ByteString.copyFrom(new byte[] {-1}); + assertThat(comparator.compare(boundedLow, boundedHigh)).isLessThan(0); + assertThat(comparator.compare(boundedHigh, boundedLow)).isGreaterThan(0); + assertThat(comparator.compare(literalLow, boundedHigh)).isLessThan(0); + assertThat(comparator.compare(boundedHigh, literalLow)).isGreaterThan(0); + assertThat(comparator.compare(boundedLow, literalHigh)).isLessThan(0); + assertThat(comparator.compare(literalHigh, boundedLow)).isGreaterThan(0); + } + + @Test public void testCompare_heterogeneousByteStrings_fallbackPath() throws Exception { Comparator<ByteString> comparator = ByteString.unsignedLexicographicalComparator(); - byte[] raw = new byte[] {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; - ByteString literal = ByteString.copyFrom(raw); - // Substring creates BoundedByteString (LeafByteString but NOT LiteralByteString) - ByteString bounded1 = ByteString.copyFrom(raw).substring(0, 10); - ByteString bounded2 = ByteString.copyFrom(raw).substring(1, 11); + byte[] raw = new byte[] {99, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 99}; + ByteString literal1 = ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}); + ByteString literal2 = ByteString.copyFrom(new byte[] {2, 3, 4, 5, 6, 7, 8, 9, 10, 11}); + ByteString bounded1 = ByteString.copyFrom(raw).substring(1, 11); + ByteString bounded2 = ByteString.copyFrom(raw).substring(2, 12); + ByteString rope1 = + RopeByteString.newInstanceForTest( + ByteString.copyFrom(new byte[] {1, 2, 3, 4, 5}), + ByteString.copyFrom(new byte[] {6, 7, 8, 9, 10})); + ByteString rope2 = + RopeByteString.newInstanceForTest( + ByteString.copyFrom(new byte[] {2, 3, 4, 5, 6}), + ByteString.copyFrom(new byte[] {7, 8, 9, 10, 11})); - // Test Literal vs Bounded - assertWithMessage("Literal vs Bounded equal") - .that(comparator.compare(literal.substring(0, 10), bounded1)) - .isEqualTo(0); - assertWithMessage("Literal vs Bounded different") - .that(comparator.compare(literal.substring(0, 10), bounded2) < 0) - .isTrue(); - assertWithMessage("Bounded vs Literal different") - .that(comparator.compare(bounded2, literal.substring(0, 10)) > 0) - .isTrue(); + // Literal vs Rope + assertThat(comparator.compare(literal1, rope1)).isEqualTo(0); + assertThat(comparator.compare(rope1, literal1)).isEqualTo(0); + assertThat(comparator.compare(literal1, rope2)).isLessThan(0); + assertThat(comparator.compare(rope2, literal1)).isGreaterThan(0); + assertThat(comparator.compare(rope1, literal2)).isLessThan(0); + assertThat(comparator.compare(literal2, rope1)).isGreaterThan(0); + + // Bounded vs Rope + assertThat(comparator.compare(bounded1, rope1)).isEqualTo(0); + assertThat(comparator.compare(rope1, bounded1)).isEqualTo(0); + assertThat(comparator.compare(bounded1, rope2)).isLessThan(0); + assertThat(comparator.compare(rope2, bounded1)).isGreaterThan(0); + assertThat(comparator.compare(rope1, bounded2)).isLessThan(0); + assertThat(comparator.compare(bounded2, rope1)).isGreaterThan(0); + + // Rope vs Rope + assertThat(comparator.compare(rope1, rope1)).isEqualTo(0); + assertThat(comparator.compare(rope1, rope2)).isLessThan(0); + assertThat(comparator.compare(rope2, rope1)).isGreaterThan(0); } @Test